From 373aa5110536eaef50f8277df6e499e7861ca21d Mon Sep 17 00:00:00 2001 From: Jeremy Kuhne Date: Sat, 24 Feb 2018 21:33:34 -0800 Subject: [PATCH 1/3] Path tests overhaul Path tests weren't running due to a test infra bug (too many test methods in one class apparently). Cleaned up path tests, fixing issues and adding coverage. --- .../System.Runtime.Extensions.Tests.csproj | 9 +- .../tests/System/IO/Path.Combine.cs | 2 +- .../tests/System/IO/PathTests.Unix.cs | 58 ++ .../tests/System/IO/PathTests.Windows.cs | 358 +++++++ .../System/IO/PathTests.Windows.netcoreapp.cs | 37 + .../System/IO/PathTests.Windows.netfx.cs | 111 ++ .../tests/System/IO/PathTests.cs | 970 ++---------------- .../tests/System/IO/PathTests.netcoreapp.cs | 259 +++-- .../tests/System/IO/PathTestsBase.cs | 222 ++++ .../System/IO/PathTestsBase.netcoreapp.cs | 30 + .../tests/TestHelpers.cs | 37 + 11 files changed, 1056 insertions(+), 1037 deletions(-) create mode 100644 src/System.Runtime.Extensions/tests/System/IO/PathTests.Unix.cs create mode 100644 src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.cs create mode 100644 src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netcoreapp.cs create mode 100644 src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netfx.cs create mode 100644 src/System.Runtime.Extensions/tests/System/IO/PathTestsBase.cs create mode 100644 src/System.Runtime.Extensions/tests/System/IO/PathTestsBase.netcoreapp.cs create mode 100644 src/System.Runtime.Extensions/tests/TestHelpers.cs diff --git a/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj b/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj index 33507971e713..a8068ffed913 100644 --- a/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj +++ b/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj @@ -22,6 +22,9 @@ + + + @@ -30,11 +33,15 @@ + + + + @@ -56,7 +63,7 @@ - + diff --git a/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs b/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs index 292c5480d2e5..382732253fc6 100644 --- a/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs +++ b/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs @@ -7,7 +7,7 @@ namespace System.IO.Tests { - public static partial class PathTests + public class PathTests_Combine { private static readonly char s_separator = Path.DirectorySeparatorChar; diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Unix.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests.Unix.cs new file mode 100644 index 000000000000..6a549e54c10a --- /dev/null +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests.Unix.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.IO.Tests +{ + [PlatformSpecific(TestPlatforms.AnyUnix)] + public class PathTests_Unix : PathTestsBase + { + [Theory, + MemberData(nameof(TestData_GetPathRoot_Unc)), + MemberData(nameof(TestData_GetPathRoot_DevicePaths))] + public static void GetPathRoot(string value, string expected) + { + // UNCs and device paths have no special meaning in Unix + Assert.Empty(Path.GetPathRoot(value)); + } + + [Theory, + InlineData("B:", "B:"), + InlineData("A:.", "A:.")] + public void GetFileName_Volume(string path, string expected) + { + // No such thing as a drive relative path on Unix. + Assert.Equal(expected, Path.GetFileName(path)); + Assert.Equal(expected, new string(Path.GetFileName(path.AsSpan()))); + } + + [Theory, + InlineData("/tmp/", "/tmp"), + InlineData("/tmp/", "/tmp/"), + InlineData("/", "/"), + InlineData("/var/tmp/", "/var/tmp"), + InlineData("/var/tmp/", "/var/tmp/"), + InlineData("~/", "~"), + InlineData("~/", "~/"), + InlineData(".tmp/", ".tmp"), + InlineData("./tmp/", "./tmp"), + InlineData("/home/someuser/sometempdir/", "/home/someuser/sometempdir/"), + InlineData("/home/someuser/some tempdir/", "/home/someuser/some tempdir/"), + InlineData("/tmp/", null)] + public void GetTempPath_SetEnvVar_Unix(string expected, string newTempPath) + { + GetTempPath_SetEnvVar("TMPDIR", expected, newTempPath); + } + + [Fact] + public void GetFullPath_Unix_Whitespace() + { + string curDir = Directory.GetCurrentDirectory(); + Assert.Equal("/ / ", Path.GetFullPath("/ // ")); + Assert.Equal(Path.Combine(curDir, " "), Path.GetFullPath(" ")); + Assert.Equal(Path.Combine(curDir, "\r\n"), Path.GetFullPath("\r\n")); + } + } +} diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.cs new file mode 100644 index 000000000000..8e8758ae3a94 --- /dev/null +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.cs @@ -0,0 +1,358 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using Xunit; + +namespace System.IO.Tests +{ + [PlatformSpecific(TestPlatforms.Windows)] + public partial class PathTests_Windows : PathTestsBase + { + public void GetDirectoryName_DevicePath() + { + if (PathFeatures.IsUsingLegacyPathNormalization()) + { + Assert.Equal(@"\\?\C:", Path.GetDirectoryName(@"\\?\C:\foo")); + } + else + { + Assert.Equal(@"\\?\C:\", Path.GetDirectoryName(@"\\?\C:\foo")); + } + } + + [Theory, MemberData(nameof(TestData_GetDirectoryName_Windows))] + public void GetDirectoryName(string path, string expected) + { + Assert.Equal(expected, Path.GetDirectoryName(path)); + } + + [Theory, + InlineData("B:", ""), + InlineData("A:.", ".")] + public static void GetFileName_Volume(string path, string expected) + { + // With a valid drive letter followed by a colon, we have a root, but only on Windows. + Assert.Equal(expected, Path.GetFileName(path)); + } + + [ActiveIssue(27269, TargetFrameworkMonikers.Netcoreapp)] + [Theory, + MemberData(nameof(TestData_GetPathRoot_Windows)), + MemberData(nameof(TestData_GetPathRoot_Unc)), + MemberData(nameof(TestData_GetPathRoot_DevicePaths))] + public void GetPathRoot_Windows(string value, string expected) + { + Assert.Equal(expected, Path.GetPathRoot(value)); + + if (value.Length != expected.Length) + { + // The string overload normalizes the separators + Assert.Equal(expected, Path.GetPathRoot(value.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))); + + // UNCs and device paths will have their semantics changed if we double up separators + if (!value.StartsWith(@"\\")) + Assert.Equal(expected, Path.GetPathRoot(value.Replace(@"\", @"\\"))); + } + } + + [Theory, + InlineData(@"C:\Users\someuser\AppData\Local\Temp\", @"C:\Users\someuser\AppData\Local\Temp"), + InlineData(@"C:\Users\someuser\AppData\Local\Temp\", @"C:\Users\someuser\AppData\Local\Temp\"), + InlineData(@"C:\", @"C:\"), + InlineData(@"C:\tmp\", @"C:\tmp"), + InlineData(@"C:\tmp\", @"C:\tmp\")] + public void GetTempPath_SetEnvVar(string expected, string newTempPath) + { + GetTempPath_SetEnvVar("TMP", expected, newTempPath); + } + + [Theory, MemberData(nameof(TestData_Spaces))] + public void GetFullPath_TrailingSpacesCut(string component) + { + // Windows cuts off any simple white space added to a path + string path = "C:\\Test" + component; + Assert.Equal("C:\\Test", Path.GetFullPath(path)); + } + + [Fact] + public void GetFullPath_NormalizedLongPathTooLong() + { + // Try out a long path that normalizes down to more than MaxPath + string curDir = Directory.GetCurrentDirectory(); + const int Iters = 260; + var longPath = new StringBuilder(curDir, curDir.Length + (Iters * 4)); + for (int i = 0; i < Iters; i++) + { + longPath.Append(Path.DirectorySeparatorChar).Append('a').Append(Path.DirectorySeparatorChar).Append('.'); + } + + if (PathFeatures.AreAllLongPathsAvailable()) + { + // Now no longer throws unless over ~32K + Assert.NotNull(Path.GetFullPath(longPath.ToString())); + } + else + { + Assert.Throws(() => Path.GetFullPath(longPath.ToString())); + } + } + + [Theory, + InlineData(@"C:..."), + InlineData(@"C:...\somedir"), + InlineData(@"\.. .\"), + InlineData(@"\. .\"), + InlineData(@"\ .\")] + public void GetFullPath_LegacyArgumentExceptionPaths(string path) + { + if (PathFeatures.IsUsingLegacyPathNormalization()) + { + // We didn't allow these paths on < 4.6.2 + AssertExtensions.Throws(null, () => Path.GetFullPath(path)); + } + else + { + // These paths are legitimate Windows paths that can be created without extended syntax. + // We now allow them through. + Path.GetFullPath(path); + } + } + + [Fact] + public void GetFullPath_MaxPathNotTooLong() + { + string value = @"C:\" + new string('a', 255) + @"\"; + if (PathFeatures.AreAllLongPathsAvailable()) + { + // Shouldn't throw anymore + Path.GetFullPath(value); + } + else + { + Assert.Throws(() => Path.GetFullPath(value)); + } + } + + [Fact] + public void GetFullPath_PathTooLong() + { + Assert.Throws(() => Path.GetFullPath(@"C:\" + new string('a', short.MaxValue) + @"\")); + } + + [Theory, + InlineData(@"C:\", @"C:\"), + InlineData(@"C:\.", @"C:\"), + InlineData(@"C:\..", @"C:\"), + InlineData(@"C:\..\..", @"C:\"), + InlineData(@"C:\A\..", @"C:\"), + InlineData(@"C:\..\..\A\..", @"C:\")] + public void GetFullPath_RelativeRoot(string path, string expected) + { + Assert.Equal(Path.GetFullPath(path), expected); + } + + [Fact] + public void GetFullPath_StrangeButLegalPaths() + { + // These are legal and creatable without using extended syntax if you use a trailing slash + // (such as "md ...\"). We used to filter these out, but now allow them to prevent apps from + // being blocked when they hit these paths. + string curDir = Directory.GetCurrentDirectory(); + if (PathFeatures.IsUsingLegacyPathNormalization()) + { + // Legacy path Path.GetFullePath() ignores . when there is less or more that two, when there is .. in the path it returns one directory up. + Assert.Equal( + Path.GetFullPath(curDir + Path.DirectorySeparatorChar), + Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ". " + Path.DirectorySeparatorChar)); + Assert.Equal( + Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar), + Path.GetFullPath(curDir + Path.DirectorySeparatorChar + "..." + Path.DirectorySeparatorChar)); + Assert.Equal( + Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar), + Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ".. " + Path.DirectorySeparatorChar)); + } + else + { + Assert.NotEqual( + Path.GetFullPath(curDir + Path.DirectorySeparatorChar), + Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ". " + Path.DirectorySeparatorChar)); + Assert.NotEqual( + Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar), + Path.GetFullPath(curDir + Path.DirectorySeparatorChar + "..." + Path.DirectorySeparatorChar)); + Assert.NotEqual( + Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar), + Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ".. " + Path.DirectorySeparatorChar)); + } + } + + [Theory, + InlineData(@"\\?\C:\ "), + InlineData(@"\\?\C:\ \ "), + InlineData(@"\\?\C:\ ."), + InlineData(@"\\?\C:\ .."), + InlineData(@"\\?\C:\..."), + InlineData(@"\\?\GLOBALROOT\"), + InlineData(@"\\?\"), + InlineData(@"\\?\."), + InlineData(@"\\?\.."), + InlineData(@"\\?\\"), + InlineData(@"\\?\C:\\"), + InlineData(@"\\?\C:\|"), + InlineData(@"\\?\C:\."), + InlineData(@"\\?\C:\.."), + InlineData(@"\\?\C:\Foo1\."), + InlineData(@"\\?\C:\Foo2\.."), + InlineData(@"\\?\UNC\"), + InlineData(@"\\?\UNC\server1"), + InlineData(@"\\?\UNC\server2\"), + InlineData(@"\\?\UNC\server3\\"), + InlineData(@"\\?\UNC\server4\.."), + InlineData(@"\\?\UNC\server5\share\."), + InlineData(@"\\?\UNC\server6\share\.."), + InlineData(@"\\?\UNC\a\b\\"), + InlineData(@"\\.\"), + InlineData(@"\\.\."), + InlineData(@"\\.\.."), + InlineData(@"\\.\\"), + InlineData(@"\\.\C:\\"), + InlineData(@"\\.\C:\|"), + InlineData(@"\\.\C:\."), + InlineData(@"\\.\C:\.."), + InlineData(@"\\.\C:\Foo1\."), + InlineData(@"\\.\C:\Foo2\..")] + public void GetFullPath_ValidExtendedPaths(string path) + { + if (PathFeatures.IsUsingLegacyPathNormalization()) + { + // Legacy Path doesn't support any of these paths. + AssertExtensions.ThrowsAny(() => Path.GetFullPath(path)); + return; + } + + // None of these should throw + if (path.StartsWith(@"\\?\")) + { + Assert.Equal(path, Path.GetFullPath(path)); + } + else + { + Path.GetFullPath(path); + } + } + + [Theory, + InlineData(@"\\.\UNC\"), + InlineData(@"\\.\UNC\LOCALHOST"), + InlineData(@"\\.\UNC\localHOST\"), + InlineData(@"\\.\UNC\LOcaLHOST\\"), + InlineData(@"\\.\UNC\lOCALHOST\.."), + InlineData(@"\\.\UNC\LOCALhost\share\."), + InlineData(@"\\.\UNC\loCALHOST\share\.."), + InlineData(@"\\.\UNC\a\b\\")] + public static void GetFullPath_ValidLegacy_ValidExtendedPaths(string path) + { + // should not throw + Path.GetFullPath(path); + } + + [Theory, + // https://github.com/dotnet/corefx/issues/11965 + InlineData(@"\\LOCALHOST\share\test.txt.~SS", @"\\LOCALHOST\share\test.txt.~SS"), + InlineData(@"\\LOCALHOST\share1", @"\\LOCALHOST\share1"), + InlineData(@"\\LOCALHOST\share3\dir", @"\\LOCALHOST\share3\dir"), + InlineData(@"\\LOCALHOST\share4\.", @"\\LOCALHOST\share4"), + InlineData(@"\\LOCALHOST\share5\..", @"\\LOCALHOST\share5"), + InlineData(@"\\LOCALHOST\share6\ ", @"\\LOCALHOST\share6\"), + InlineData(@"\\LOCALHOST\ share7\", @"\\LOCALHOST\ share7\"), + InlineData(@"\\?\UNC\LOCALHOST\share8\test.txt.~SS", @"\\?\UNC\LOCALHOST\share8\test.txt.~SS"), + InlineData(@"\\?\UNC\LOCALHOST\share9", @"\\?\UNC\LOCALHOST\share9"), + InlineData(@"\\?\UNC\LOCALHOST\shareA\dir", @"\\?\UNC\LOCALHOST\shareA\dir"), + InlineData(@"\\?\UNC\LOCALHOST\shareB\. ", @"\\?\UNC\LOCALHOST\shareB\. "), + InlineData(@"\\?\UNC\LOCALHOST\shareC\.. ", @"\\?\UNC\LOCALHOST\shareC\.. "), + InlineData(@"\\?\UNC\LOCALHOST\shareD\ ", @"\\?\UNC\LOCALHOST\shareD\ "), + InlineData(@"\\.\UNC\LOCALHOST\ shareE\", @"\\.\UNC\LOCALHOST\ shareE\"), + InlineData(@"\\.\UNC\LOCALHOST\shareF\test.txt.~SS", @"\\.\UNC\LOCALHOST\shareF\test.txt.~SS"), + InlineData(@"\\.\UNC\LOCALHOST\shareG", @"\\.\UNC\LOCALHOST\shareG"), + InlineData(@"\\.\UNC\LOCALHOST\shareH\dir", @"\\.\UNC\LOCALHOST\shareH\dir"), + InlineData(@"\\.\UNC\LOCALHOST\shareK\ ", @"\\.\UNC\LOCALHOST\shareK\"), + InlineData(@"\\.\UNC\LOCALHOST\ shareL\", @"\\.\UNC\LOCALHOST\ shareL\")] + public void GetFullPath_UNC_Valid(string path, string expected) + { + if (path.StartsWith(@"\\?\") && PathFeatures.IsUsingLegacyPathNormalization()) + { + AssertExtensions.Throws(null, () => Path.GetFullPath(path)); + } + else + { + Assert.Equal(expected, Path.GetFullPath(path)); + } + } + + [Theory, + InlineData(@"\\.\UNC\LOCALHOST\shareI\. ", @"\\.\UNC\LOCALHOST\shareI\", @"\\.\UNC\LOCALHOST\shareI"), + InlineData(@"\\.\UNC\LOCALHOST\shareJ\.. ", @"\\.\UNC\LOCALHOST\shareJ\", @"\\.\UNC\LOCALHOST")] + public static void GetFullPath_Windows_UNC_Valid_LegacyPathSupport(string path, string normalExpected, string legacyExpected) + { + string expected = PathFeatures.IsUsingLegacyPathNormalization() ? legacyExpected : normalExpected; + Assert.Equal(expected, Path.GetFullPath(path)); + } + + [Fact] + public static void GetFullPath_Windows_83Paths() + { + // Create a temporary file name with a name longer than 8.3 such that it'll need to be shortened. + string tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".txt"); + File.Create(tempFilePath).Dispose(); + try + { + // Get its short name + var sb = new StringBuilder(260); + if (GetShortPathName(tempFilePath, sb, sb.Capacity) > 0) // only proceed if we could successfully create the short name + { + string shortName = sb.ToString(); + + // Make sure the shortened name expands back to the original one + // Sometimes shortening or GetFullPath is changing the casing of "temp" on some test machines: normalize both sides + tempFilePath = Regex.Replace(tempFilePath, @"\\temp\\", @"\TEMP\", RegexOptions.IgnoreCase); + shortName = Regex.Replace(Path.GetFullPath(shortName), @"\\temp\\", @"\TEMP\", RegexOptions.IgnoreCase); + Assert.Equal(tempFilePath, shortName); + + // Should work with device paths that aren't well-formed extended syntax + if (!PathFeatures.IsUsingLegacyPathNormalization()) + { + Assert.Equal(@"\\.\" + tempFilePath, Path.GetFullPath(@"\\.\" + shortName)); + Assert.Equal(@"\\?\" + tempFilePath, Path.GetFullPath(@"//?/" + shortName)); + + // Shouldn't mess with well-formed extended syntax + Assert.Equal(@"\\?\" + shortName, Path.GetFullPath(@"\\?\" + shortName)); + } + + // Validate case where short name doesn't expand to a real file + string invalidShortName = @"S:\DOESNT~1\USERNA~1.RED\LOCALS~1\Temp\bg3ylpzp"; + Assert.Equal(invalidShortName, Path.GetFullPath(invalidShortName)); + + // Same thing, but with a long path that normalizes down to a short enough one + const int Iters = 1000; + var shortLongName = new StringBuilder(invalidShortName, invalidShortName.Length + (Iters * 2)); + for (int i = 0; i < Iters; i++) + { + shortLongName.Append(Path.DirectorySeparatorChar).Append('.'); + } + Assert.Equal(invalidShortName, Path.GetFullPath(shortLongName.ToString())); + } + } + finally + { + File.Delete(tempFilePath); + } + } + + // Windows-only P/Invoke to create 8.3 short names from long names + [DllImport("kernel32.dll", EntryPoint = "GetShortPathNameW", CharSet = CharSet.Unicode)] + private static extern uint GetShortPathName(string lpszLongPath, StringBuilder lpszShortPath, int cchBuffer); + } +} diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netcoreapp.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netcoreapp.cs new file mode 100644 index 000000000000..43f24ef7ce8c --- /dev/null +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netcoreapp.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.IO.Tests +{ + public partial class PathTests_Windows : PathTestsBase + { + [Theory, + MemberData(nameof(TestData_GetPathRoot_Windows)), + MemberData(nameof(TestData_GetPathRoot_Unc)), + MemberData(nameof(TestData_GetPathRoot_DevicePaths))] + public void GetPathRoot_Span(string value, string expected) + { + Assert.Equal(expected, new string(Path.GetPathRoot(value.AsSpan()))); + Assert.True(Path.IsPathRooted(value.AsSpan())); + } + + [Theory, MemberData(nameof(TestData_UnicodeWhiteSpace))] + public void GetFullPath_UnicodeWhiteSpaceStays(string component) + { + // When not NetFX full path should not cut off component + string path = "C:\\Test" + component; + Assert.Equal(path, Path.GetFullPath(path)); + } + + [Theory, MemberData(nameof(TestData_Periods))] + public void GetFullPath_TrailingPeriodsCut(string component) + { + // Windows cuts off any simple white space added to a path + string path = "C:\\Test" + component; + Assert.Equal("C:\\Test", Path.GetFullPath(path)); + } + } +} diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netfx.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netfx.cs new file mode 100644 index 000000000000..8fb382e3520c --- /dev/null +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netfx.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public class PathTests_NetFX : PathTestsBase + { + [Theory, + MemberData(nameof(TestData_EmbeddedNull)), + MemberData(nameof(TestData_EmptyString)), + MemberData(nameof(TestData_ControlChars))] + public void GetDirectoryName_ArgumentExceptions(string path) + { + // In NetFX we normalize and check invalid path chars + AssertExtensions.Throws("path", null, () => Path.GetDirectoryName(path)); + } + + [Theory, + MemberData(nameof(TestData_ControlChars)), + MemberData(nameof(TestData_EmbeddedNull))] + public void IsPathRooted_ArgumentExceptions(string path) + { + // In NetFX we check invalid path chars + AssertExtensions.Throws("path", null, () => Path.IsPathRooted(path)); + } + + [Theory, + MemberData(nameof(TestData_Spaces)), + MemberData(nameof(TestData_UnicodeWhiteSpace)), + MemberData(nameof(TestData_EmptyString))] + public void IsPathRooted_NegativeCases(string path) + { + Assert.False(Path.IsPathRooted(path)); + } + + [Theory, + MemberData(nameof(TestData_InvalidDriveLetters)), + MemberData(nameof(TestData_ValidDriveLetters))] + public void IsPathRooted(string path) + { + Assert.True(Path.IsPathRooted(path)); + } + + [Theory, + InlineData(@" C:\dir\baz", @"C:\dir"), + InlineData(@" C:\dir\baz", @"C:\dir")] + public void GetDirectoryName_SkipSpaces(string path, string expected) + { + // In very specific cases we would trim leading spaces + Assert.Equal(expected, Path.GetDirectoryName(path)); + } + + [Fact] + public void GetPathRoot_EmptyThrows_Desktop() + { + AssertExtensions.Throws("path", null, () => Path.GetPathRoot(string.Empty)); + } + + [Fact] + public void GetInvalidPathChars() + { + Assert.All(Path.GetInvalidPathChars(), c => + { + string bad = c.ToString(); + AssertExtensions.Throws("path", null, () => Path.ChangeExtension(bad, "ok")); + AssertExtensions.Throws("path", null, () => Path.Combine(bad, "ok")); + AssertExtensions.Throws("path", null, () => Path.Combine("ok", "ok", bad)); + AssertExtensions.Throws("path", null, () => Path.Combine("ok", "ok", bad, "ok")); + AssertExtensions.Throws("path", null, () => Path.Combine(bad, bad, bad, bad, bad)); + AssertExtensions.Throws("path", null, () => Path.GetDirectoryName(bad)); + AssertExtensions.Throws("path", null, () => Path.GetExtension(bad)); + AssertExtensions.Throws("path", null, () => Path.GetFileName(bad)); + AssertExtensions.Throws("path", null, () => Path.GetFileNameWithoutExtension(bad)); + AssertExtensions.Throws(c == 124 ? null : "path", null, () => Path.GetFullPath(bad)); + AssertExtensions.Throws("path", null, () => Path.GetPathRoot(bad)); + AssertExtensions.Throws("path", null, () => Path.IsPathRooted(bad)); + }); + } + + [Theory, MemberData(nameof(TestData_NonDriveColonPaths))] + public void GetFullPath_NotSupportedColons(string path) + { + // Throws via our invalid colon filtering (as part of FileIOPermissions) + AssertExtensions.ThrowsAny(() => Path.GetFullPath(path)); + } + + [Theory, + MemberData(nameof(TestData_Wildcards)), + MemberData(nameof(TestData_ExtendedWildcards))] + public void GetFullPath_Wildcards(char wildcard) + { + AssertExtensions.Throws("path", null, () => Path.GetFullPath("test" + wildcard + "ing")); + } + + [Theory, MemberData(nameof(TestData_InvalidUnc))] + public void GetFullPath_UNC_Invalid(string invalidPath) + { + AssertExtensions.Throws(null, () => Path.GetFullPath(invalidPath)); + } + } +} diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests.cs index e5974bc34941..26cdc0afee1d 100644 --- a/src/System.Runtime.Extensions/tests/System/IO/PathTests.cs +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests.cs @@ -1,32 +1,30 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Text; -using System.Text.RegularExpressions; using Xunit; namespace System.IO.Tests { - public static partial class PathTests + public partial class PathTests : PathTestsBase { - [Theory] - [InlineData(null, null, null)] - [InlineData(null, "exe", null)] - [InlineData("", "", "")] - [InlineData("file.exe", null, "file")] - [InlineData("file.exe", "", "file.")] - [InlineData("file", "exe", "file.exe")] - [InlineData("file", ".exe", "file.exe")] - [InlineData("file.txt", "exe", "file.exe")] - [InlineData("file.txt", ".exe", "file.exe")] - [InlineData("file.txt.bin", "exe", "file.txt.exe")] - [InlineData("dir/file.t", "exe", "dir/file.exe")] - [InlineData("dir/file.exe", "t", "dir/file.t")] - [InlineData("dir/file", "exe", "dir/file.exe")] - public static void ChangeExtension(string path, string newExtension, string expected) + [Theory, + InlineData(null, null, null), + InlineData(null, "exe", null), + InlineData("", "", ""), + InlineData("file.exe", null, "file"), + InlineData("file.exe", "", "file."), + InlineData("file", "exe", "file.exe"), + InlineData("file", ".exe", "file.exe"), + InlineData("file.txt", "exe", "file.exe"), + InlineData("file.txt", ".exe", "file.exe"), + InlineData("file.txt.bin", "exe", "file.txt.exe"), + InlineData("dir/file.t", "exe", "dir/file.exe"), + InlineData("dir/file.exe", "t", "dir/file.t"), + InlineData("dir/file", "exe", "dir/file.exe")] + public void ChangeExtension(string path, string newExtension, string expected) { if (expected != null) expected = expected.Replace('/', Path.DirectorySeparatorChar); @@ -36,286 +34,78 @@ public static void ChangeExtension(string path, string newExtension, string expe } [Fact] - [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - public static void GetDirectoryName_EmptyThrows_Desktop() - { - AssertExtensions.Throws("path", null, () => Path.GetDirectoryName(string.Empty)); - } - - [Fact] - [ActiveIssue(27269)] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - public static void GetDirectoryName_Empty_Core() - { - Assert.Null(Path.GetDirectoryName(string.Empty)); - } - - [Theory, - InlineData(" "), - InlineData("\r\n")] - [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - public static void GetDirectoryName_SpaceOrControlCharsThrowOnWindows(string path) - { - Action action = () => Path.GetDirectoryName(path); - if (PlatformDetection.IsWindows) - { - AssertExtensions.Throws("path", null, () => Path.GetDirectoryName(path)); - } - else - { - // These are valid paths on Unix - action(); - } - } - - [Fact] - [ActiveIssue(27269)] - public static void GetDirectoryName_Space_Core() - { - Assert.Null(Path.GetDirectoryName(" ")); - } - - public static TheoryData GetDirectoryName_NonControl_Test_Data => new TheoryData - { - { "\u00A0" }, // Non-breaking Space - { "\u2028" }, // Line separator - { "\u2029" }, // Paragraph separator - }; - - [Theory, MemberData(nameof(GetDirectoryName_NonControl_Test_Data))] - public static void GetDirectoryName_NonControl(string path) - { - Assert.Equal(string.Empty, Path.GetDirectoryName(path)); - } - - [Theory, MemberData(nameof(GetDirectoryName_NonControl_Test_Data))] - public static void GetDirectoryName_NonControlWithSeparator(string path) + public void GetDirectoryName_NullReturnsNull() { - Assert.Equal(path, Path.GetDirectoryName(Path.Combine(path, path))); + Assert.Null(Path.GetDirectoryName(null)); } - public static TheoryData GetDirectoryName_Test_Data => new TheoryData - { - { null, null }, - { ".", "" }, - { "..", "" }, - { "baz", "" }, - }; - - [Theory, MemberData(nameof(GetDirectoryName_Test_Data))] - public static void GetDirectoryName(string path, string expected) - { - Assert.Equal(expected, Path.GetDirectoryName(path)); - } - - public static TheoryData GetDirectoryName_Windows_Test_Data => new TheoryData - { - { @"dir/baz", "dir" }, - { @"dir\baz", "dir" }, - { @"dir\baz\bar", @"dir\baz" }, - { @"..\..\files.txt", @"..\.." }, - { @"C:\", null }, - { @"C:", null }, - // { @"dir\\baz", "dir" }, https://github.com/dotnet/corefx/issues/27269 - { @"C:\foo", @"C:\" }, - { @"\foo", @"\" }, - { @"C:foo", @"C:" } - }; - - public static TheoryData GetDirectoryName_Windows_string_Test_Data => new TheoryData - { - // { @" C:\dir\baz", @"C:\dir" }, https://github.com/dotnet/corefx/issues/27269 - { @" dir\baz", " dir" }, - // { @" C:\dir\baz", @"C:\dir" }, https://github.com/dotnet/corefx/issues/27269 - }; - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific paths - [Theory, MemberData(nameof(GetDirectoryName_Windows_Test_Data)), MemberData(nameof(GetDirectoryName_Windows_string_Test_Data))] - public static void GetDirectoryName_Windows(string path, string expected) - { - Assert.Equal(expected, Path.GetDirectoryName(path)); - } - - [PlatformSpecific(TestPlatforms.Windows)] - public static void GetDirectoryName_WindowsDevicePath() - { - if (PathFeatures.IsUsingLegacyPathNormalization()) - { - Assert.Equal(@"\\?\C:", Path.GetDirectoryName(@"\\?\C:\foo")); - } - else - { - Assert.Equal(@"\\?\C:\", Path.GetDirectoryName(@"\\?\C:\foo")); - } - } - - public static TheoryData GetDirectoryName_Unix_Test_Data => new TheoryData - { - { @"dir/baz", @"dir" }, - { @"dir\baz", @"" }, - { @"dir/baz/bar", @"dir/baz" }, - { @"../../files.txt", @"../.." }, - { @"/", null }, - }; - - public static TheoryData GetDirectoryName_Unix_String_Test_Data => new TheoryData - { - { @"dir//baz", @"dir" }, - }; - - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests Unix-specific paths - [Theory, MemberData(nameof(GetDirectoryName_Unix_Test_Data)), MemberData(nameof(GetDirectoryName_Unix_String_Test_Data))] - public static void GetDirectoryName_Unix(string path, string expected) + [Theory, MemberData(nameof(TestData_GetDirectoryName))] + public void GetDirectoryName(string path, string expected) { Assert.Equal(expected, Path.GetDirectoryName(path)); } [Fact] - public static void GetDirectoryName_CurrentDirectory() + public void GetDirectoryName_CurrentDirectory() { string curDir = Directory.GetCurrentDirectory(); Assert.Equal(curDir, Path.GetDirectoryName(Path.Combine(curDir, "baz"))); - + Assert.Equal(null, Path.GetDirectoryName(Path.GetPathRoot(curDir))); } - public static TheoryData GetDirectoryName_ControlCharacters_Unix_Test_Data => new TheoryData - { - { '\t', 1, "file" }, - { '\b', 2, "fi le" }, - { '\v', 3, "fi\nle" }, - { '\v', 3, "fi\nle" }, - }; - - [PlatformSpecific(TestPlatforms.AnyUnix)] // Checks Unix-specific special characters in directory path - [Theory, MemberData(nameof(GetDirectoryName_ControlCharacters_Unix_Test_Data))] - public static void GetDirectoryName_ControlCharacters_Unix(char ch, int count, string file) + [Fact] + public void GetExtension_Null() { - Assert.Equal(new string(ch, count), Path.GetDirectoryName(Path.Combine(new string(ch, count), file))); + Assert.Null(Path.GetExtension(null)); } - public static TheoryData GetExtension_Test_Data => new TheoryData + [Theory, MemberData(nameof(TestData_GetExtension))] + public void GetExtension(string path, string expected) { - { "file.exe", ".exe" }, - { "file", "" }, - { null, null }, - { "file.", "" }, - { "file.s", ".s" }, - { "test/file", "" }, - { "test/file.extension", ".extension" }, - }; - - [Theory, MemberData(nameof(GetExtension_Test_Data))] - public static void GetExtension(string path, string expected) - { - if (path != null) - { - path = path.Replace('/', Path.DirectorySeparatorChar); - } - Assert.Equal(expected, Path.GetExtension(path)); Assert.Equal(!string.IsNullOrEmpty(expected), Path.HasExtension(path)); } - public static TheoryData GetExtension_Unix_Test_Data => new TheoryData - { - { "file.e xe", ".e xe"}, - { "file. ", ". "}, - { " file. ", ". "}, - { " file.extension", ".extension"}, - { "file.exten\tsion", ".exten\tsion" }, - }; - - [PlatformSpecific(TestPlatforms.AnyUnix)] // Checks file extension behavior on Unix - [Theory, MemberData(nameof(GetExtension_Unix_Test_Data))] - public static void GetExtension_Unix(string path, string expected) + [Fact] + public void GetFileName_Null() { - Assert.Equal(expected, Path.GetExtension(path)); - Assert.Equal(!string.IsNullOrEmpty(expected), Path.HasExtension(path)); + Assert.Null(Path.GetFileName(null)); } - public static IEnumerable GetFileName_TestData() + [Fact] + public void GetFileName_Empty() { - yield return new object[] { null, null }; - yield return new object[] { "", "" }; - yield return new object[] { ".", "." }; - yield return new object[] { "..", ".." }; - yield return new object[] { "file", "file" }; - yield return new object[] { "file.", "file." }; - yield return new object[] { "file.exe", "file.exe" }; - yield return new object[] { Path.Combine("baz", "file.exe"), "file.exe" }; - yield return new object[] { Path.Combine("bar", "baz", "file.exe"), "file.exe" }; - yield return new object[] { Path.Combine("bar", "baz", "file.exe") + Path.DirectorySeparatorChar, "" }; + Assert.Empty(Path.GetFileName(string.Empty)); } - [Theory] - [MemberData(nameof(GetFileName_TestData))] - public static void GetFileName(string path, string expected) + [Theory, MemberData(nameof(TestData_GetFileName))] + public void GetFileName(string path, string expected) { Assert.Equal(expected, Path.GetFileName(path)); + Assert.Equal(expected, Path.GetFileName(Path.Combine("whizzle", path))); } - public static TheoryData GetFileName_Unix_Test_Data => new TheoryData - { - {" . "}, - {" .. "}, - {"fi le"}, - }; - - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests Unix-specific valid file names - [Theory, MemberData(nameof(GetFileName_Unix_Test_Data))] - public static void GetFileName_Unix(string file) - { - Assert.Equal(file, Path.GetFileName(file)); - } - - [PlatformSpecific(TestPlatforms.AnyUnix)] [Fact] - public static void GetFileNameWithSpaces_Unix() + public void GetFileNameWithoutExtension_Null() { - Assert.Equal("fi le", Path.GetFileName(Path.Combine("b \r\n ar", "fi le"))); + Assert.Null(Path.GetFileNameWithoutExtension(null)); } - public static IEnumerable GetFileNameWithoutExtension_TestData() - { - yield return new object[] { null, null }; - yield return new object[] { "", "" }; - yield return new object[] { "file", "file" }; - yield return new object[] { "file.exe", "file" }; - yield return new object[] { Path.Combine("bar", "baz", "file.exe"), "file" }; - yield return new object[] { Path.Combine("bar", "baz") + Path.DirectorySeparatorChar, "" }; - } - - [Theory] - [MemberData(nameof(GetFileNameWithoutExtension_TestData))] - public static void GetFileNameWithoutExtension(string path, string expected) + [Theory, MemberData(nameof(TestData_GetFileNameWithoutExtension))] + public void GetFileNameWithoutExtension(string path, string expected) { Assert.Equal(expected, Path.GetFileNameWithoutExtension(path)); } [Fact] - public static void GetPathRoot_NullReturnsNull() + public void GetPathRoot_Null() { Assert.Null(Path.GetPathRoot(null)); } [Fact] - [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - public static void GetPathRoot_EmptyThrows_Desktop() - { - AssertExtensions.Throws("path", null, () => Path.GetPathRoot(string.Empty)); - } - - [Fact] - [ActiveIssue(27269)] - [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - public static void GetPathRoot_EmptyIsNull_Core() - { - Assert.Null(Path.GetPathRoot(string.Empty)); - } - - [Fact] - public static void GetPathRoot_Basic() + public void GetPathRoot_Basic() { string cwd = Directory.GetCurrentDirectory(); Assert.Equal(cwd.Substring(0, cwd.IndexOf(Path.DirectorySeparatorChar) + 1), Path.GetPathRoot(cwd)); @@ -325,170 +115,27 @@ public static void GetPathRoot_Basic() Assert.False(Path.IsPathRooted("file.exe")); } - public static TheoryData GetPathRoot_Windows_UncAndExtended_Test_Data => new TheoryData - { - { @"\\test\unc\path\to\something", @"\\test\unc" }, - { @"\\a\b\c\d\e", @"\\a\b" }, - { @"\\a\b\", @"\\a\b" }, - { @"\\a\b", @"\\a\b" }, - { @"\\test\unc", @"\\test\unc" }, - }; - - [PlatformSpecific(TestPlatforms.Windows)] // Tests UNC - [Theory, MemberData(nameof(GetPathRoot_Windows_UncAndExtended_Test_Data))] - public static void GetPathRoot_Windows_UncAndExtended(string value, string expected) - { - Assert.True(Path.IsPathRooted(value)); - Assert.Equal(expected, Path.GetPathRoot(value)); - } - - public static TheoryData GetPathRoot_Windows_UncAndExtended_WithLegacySupport_Test_Data => new TheoryData - { - { @"\\?\UNC\test\unc", @"\\?\UNC", @"\\?\UNC\test\unc\path\to\something" }, - { @"\\?\UNC\test\unc", @"\\?\UNC", @"\\?\UNC\test\unc" }, - { @"\\?\UNC\a\b1", @"\\?\UNC", @"\\?\UNC\a\b1" }, - { @"\\?\UNC\a\b2", @"\\?\UNC", @"\\?\UNC\a\b2\" }, - { @"\\?\C:\", @"\\?\C:", @"\\?\C:\foo\bar.txt" }, - }; - - [Theory, MemberData(nameof(GetPathRoot_Windows_UncAndExtended_WithLegacySupport_Test_Data))] - [PlatformSpecific(TestPlatforms.Windows)] // Tests UNC - public static void GetPathRoot_Windows_UncAndExtended_WithLegacySupport(string normalExpected, string legacyExpected, string value) - { - Assert.True(Path.IsPathRooted(value)); - - string expected = PathFeatures.IsUsingLegacyPathNormalization() ? legacyExpected : normalExpected; - Assert.Equal(expected, Path.GetPathRoot(value)); - } - - public static TheoryData GetPathRoot_Windows_Test_Data => new TheoryData - { - { @"C:", @"C:" }, - { @"C:\", @"C:\" }, - { @"C:\\", @"C:\" }, - { @"C:\foo1", @"C:\" }, - { @"C:\\foo2", @"C:\" }, - }; - - public static TheoryData GetPathRoot_Windows_String_Test_Data => new TheoryData - { - { @"C://", @"C:\" }, - { @"C://foo3", @"C:\" }, - }; - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific path convention - [Theory, MemberData(nameof(GetPathRoot_Windows_Test_Data)), MemberData(nameof(GetPathRoot_Windows_String_Test_Data))] - public static void GetPathRoot_Windows(string value, string expected) - { - Assert.True(Path.IsPathRooted(value)); - Assert.Equal(expected, Path.GetPathRoot(value)); - } - - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests Unix-specific path convention [Fact] - public static void GetPathRoot_Unix() - { - // slashes are normal filename characters - string uncPath = @"\\test\unc\path\to\something"; - Assert.False(Path.IsPathRooted(uncPath)); - Assert.Equal(string.Empty, Path.GetPathRoot(uncPath)); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public static void IsPathRooted(string path) - { - Assert.False(Path.IsPathRooted(path)); - } - - public static TheoryData IsPathRooted_Windows_Invalid_Test_Data => new TheoryData - { - { @"@:\foo" }, // 064 = @ 065 = A - { @"[:\\" }, // 091 = [ 090 = Z - { @"`:\foo "}, // 096 = ` 097 = a - { @"{:\\" }, // 123 = { 122 = z - }; - - // Testing invalid drive letters !(a-zA-Z) - [PlatformSpecific(TestPlatforms.Windows)] - [Theory, MemberData(nameof(IsPathRooted_Windows_Invalid_Test_Data))] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Bug fixed on Core where it would return true if the first char is not a drive letter followed by a VolumeSeparatorChar coreclr/10297")] - public static void IsPathRooted_Windows_Invalid(string value) - { - Assert.False(Path.IsPathRooted(value)); - } - - [Fact] - public static void GetRandomFileName() - { - char[] invalidChars = Path.GetInvalidFileNameChars(); - var fileNames = new HashSet(); - for (int i = 0; i < 100; i++) - { - string s = Path.GetRandomFileName(); - Assert.Equal(s.Length, 8 + 1 + 3); - Assert.Equal(s[8], '.'); - Assert.Equal(-1, s.IndexOfAny(invalidChars)); - Assert.True(fileNames.Add(s)); - } - } - - [Fact] - [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - public static void GetInvalidPathChars() + public void GetInvalidPathChars_Invariants() { Assert.NotNull(Path.GetInvalidPathChars()); Assert.NotSame(Path.GetInvalidPathChars(), Path.GetInvalidPathChars()); Assert.Equal((IEnumerable)Path.GetInvalidPathChars(), Path.GetInvalidPathChars()); Assert.True(Path.GetInvalidPathChars().Length > 0); - Assert.All(Path.GetInvalidPathChars(), c => - { - string bad = c.ToString(); - AssertExtensions.Throws("path", null, () => Path.ChangeExtension(bad, "ok")); - AssertExtensions.Throws("path", null, () => Path.Combine(bad, "ok")); - AssertExtensions.Throws("path", null, () => Path.Combine("ok", "ok", bad)); - AssertExtensions.Throws("path", null, () => Path.Combine("ok", "ok", bad, "ok")); - AssertExtensions.Throws("path", null, () => Path.Combine(bad, bad, bad, bad, bad)); - AssertExtensions.Throws("path", null, () => Path.GetDirectoryName(bad)); - AssertExtensions.Throws("path", null, () => Path.GetExtension(bad)); - AssertExtensions.Throws("path", null, () => Path.GetFileName(bad)); - AssertExtensions.Throws("path", null, () => Path.GetFileNameWithoutExtension(bad)); - AssertExtensions.Throws(c == 124 ? null : "path", null, () => Path.GetFullPath(bad)); - AssertExtensions.Throws("path", null, () => Path.GetPathRoot(bad)); - AssertExtensions.Throws("path", null, () => Path.IsPathRooted(bad)); - }); } [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - public static void GetInvalidPathChars_Core() + public void InvalidPathChars_MatchesGetInvalidPathChars() { - Assert.NotNull(Path.GetInvalidPathChars()); - Assert.NotSame(Path.GetInvalidPathChars(), Path.GetInvalidPathChars()); - Assert.Equal((IEnumerable)Path.GetInvalidPathChars(), Path.GetInvalidPathChars()); - Assert.True(Path.GetInvalidPathChars().Length > 0); - Assert.All(Path.GetInvalidPathChars(), c => - { - string bad = c.ToString(); - Assert.Equal(bad + ".ok", Path.ChangeExtension(bad, "ok")); - Assert.Equal(bad + Path.DirectorySeparatorChar + "ok", Path.Combine(bad, "ok")); - Assert.Equal("ok" + Path.DirectorySeparatorChar + "ok" + Path.DirectorySeparatorChar + bad, Path.Combine("ok", "ok", bad)); - Assert.Equal("ok" + Path.DirectorySeparatorChar + "ok" + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + "ok", Path.Combine("ok", "ok", bad, "ok")); - Assert.Equal(bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad, Path.Combine(bad, bad, bad, bad, bad)); - Assert.Equal("", Path.GetDirectoryName(bad)); - Assert.Equal(string.Empty, Path.GetExtension(bad)); - Assert.Equal(bad, Path.GetFileName(bad)); - Assert.Equal(bad, Path.GetFileNameWithoutExtension(bad)); - AssertExtensions.Throws(c == 124 ? null : "path", null, () => Path.GetFullPath(bad)); - Assert.Equal(string.Empty, Path.GetPathRoot(bad)); - Assert.False(Path.IsPathRooted(bad)); - }); +#pragma warning disable 0618 + Assert.NotNull(Path.InvalidPathChars); + Assert.Equal(Path.GetInvalidPathChars(), Path.InvalidPathChars); + Assert.Same(Path.InvalidPathChars, Path.InvalidPathChars); +#pragma warning restore 0618 } [Fact] - public static void GetInvalidFileNameChars() + public void GetInvalidFileNameChars_Invariants() { Assert.NotNull(Path.GetInvalidFileNameChars()); Assert.NotSame(Path.GetInvalidFileNameChars(), Path.GetInvalidFileNameChars()); @@ -497,24 +144,22 @@ public static void GetInvalidFileNameChars() } [Fact] - [OuterLoop] - public static void GetInvalidFileNameChars_OtherCharsValid() + public void GetRandomFileName() { - string curDir = Directory.GetCurrentDirectory(); - var invalidChars = new HashSet(Path.GetInvalidFileNameChars()); - for (int i = 0; i < char.MaxValue; i++) + char[] invalidChars = Path.GetInvalidFileNameChars(); + var fileNames = new HashSet(); + for (int i = 0; i < 100; i++) { - char c = (char)i; - if (!invalidChars.Contains(c)) - { - string name = "file" + c + ".txt"; - Assert.Equal(Path.Combine(curDir, name), Path.GetFullPath(name)); - } + string s = Path.GetRandomFileName(); + Assert.Equal(s.Length, 8 + 1 + 3); + Assert.Equal(s[8], '.'); + Assert.Equal(-1, s.IndexOfAny(invalidChars)); + Assert.True(fileNames.Add(s)); } } [Fact] - public static void GetTempPath_Default() + public void GetTempPath_Default() { string tmpPath = Path.GetTempPath(); Assert.False(string.IsNullOrEmpty(tmpPath)); @@ -523,57 +168,8 @@ public static void GetTempPath_Default() Assert.True(Directory.Exists(tmpPath)); } - [PlatformSpecific(TestPlatforms.Windows)] // Sets environment vars with Windows-specific paths - [Theory] - [InlineData(@"C:\Users\someuser\AppData\Local\Temp\", @"C:\Users\someuser\AppData\Local\Temp")] - [InlineData(@"C:\Users\someuser\AppData\Local\Temp\", @"C:\Users\someuser\AppData\Local\Temp\")] - [InlineData(@"C:\", @"C:\")] - [InlineData(@"C:\tmp\", @"C:\tmp")] - [InlineData(@"C:\tmp\", @"C:\tmp\")] - public static void GetTempPath_SetEnvVar_Windows(string expected, string newTempPath) - { - GetTempPath_SetEnvVar("TMP", expected, newTempPath); - } - - [PlatformSpecific(TestPlatforms.AnyUnix)] // Sets environment vars with Unix-specific paths - [Theory] - [InlineData("/tmp/", "/tmp")] - [InlineData("/tmp/", "/tmp/")] - [InlineData("/", "/")] - [InlineData("/var/tmp/", "/var/tmp")] - [InlineData("/var/tmp/", "/var/tmp/")] - [InlineData("~/", "~")] - [InlineData("~/", "~/")] - [InlineData(".tmp/", ".tmp")] - [InlineData("./tmp/", "./tmp")] - [InlineData("/home/someuser/sometempdir/", "/home/someuser/sometempdir/")] - [InlineData("/home/someuser/some tempdir/", "/home/someuser/some tempdir/")] - [InlineData("/tmp/", null)] - public static void GetTempPath_SetEnvVar_Unix(string expected, string newTempPath) - { - GetTempPath_SetEnvVar("TMPDIR", expected, newTempPath); - } - - private static void GetTempPath_SetEnvVar(string envVar, string expected, string newTempPath) - { - string original = Path.GetTempPath(); - Assert.NotNull(original); - try - { - Environment.SetEnvironmentVariable(envVar, newTempPath); - Assert.Equal( - Path.GetFullPath(expected), - Path.GetFullPath(Path.GetTempPath())); - } - finally - { - Environment.SetEnvironmentVariable(envVar, original); - Assert.Equal(original, Path.GetTempPath()); - } - } - [Fact] - public static void GetTempFileName() + public void GetTempFileName() { string tmpFile = Path.GetTempFileName(); try @@ -593,33 +189,8 @@ public static void GetTempFileName() } } - [Theory] - [InlineData("\u0085")] // Next line - [InlineData("\u00A0")] // Non breaking space - [InlineData("\u2028")] // Line separator - [PlatformSpecific(TestPlatforms.Windows)] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] // not NetFX - public static void GetFullPath_NonControlWhiteSpaceStays(string component) - { - // When not NetFX full path should not cut off component - string path = "C:\\Test" + component; - Assert.Equal(path, Path.GetFullPath(path)); - } - - [Theory] - [InlineData(" ")] - [InlineData(" ")] - [InlineData(" ")] - [PlatformSpecific(TestPlatforms.Windows)] - public static void GetFullPath_TrailingSpaceCut(string component) - { - // Windows cuts off any simple white space added to a path - string path = "C:\\Test" + component; - Assert.Equal("C:\\Test", Path.GetFullPath(path)); - } - [Fact] - public static void GetFullPath_InvalidArgs() + public void GetFullPath_InvalidArgs() { Assert.Throws(() => Path.GetFullPath(null)); AssertExtensions.Throws("path", null, () => Path.GetFullPath(string.Empty)); @@ -649,421 +220,10 @@ public static IEnumerable GetFullPath_BasicExpansions_TestData() yield return new object[] { longPath.ToString(), curDir }; } - [Theory] - [MemberData(nameof(GetFullPath_BasicExpansions_TestData))] - public static void GetFullPath_BasicExpansions(string path, string expected) + [Theory, MemberData(nameof(GetFullPath_BasicExpansions_TestData))] + public void GetFullPath_BasicExpansions(string path, string expected) { Assert.Equal(expected, Path.GetFullPath(path)); } - - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests whitespace in paths on Unix - [Fact] - public static void GetFullPath_Unix_Whitespace() - { - string curDir = Directory.GetCurrentDirectory(); - Assert.Equal("/ / ", Path.GetFullPath("/ // ")); - Assert.Equal(Path.Combine(curDir, " "), Path.GetFullPath(" ")); - Assert.Equal(Path.Combine(curDir, "\r\n"), Path.GetFullPath("\r\n")); - } - - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests URIs as valid file names - [Theory] - [InlineData("http://www.microsoft.com")] - [InlineData("file://somefile")] - public static void GetFullPath_Unix_URIsAsFileNames(string uriAsFileName) - { - // URIs are valid filenames, though the multiple slashes will be consolidated in GetFullPath - Assert.Equal( - Path.Combine(Directory.GetCurrentDirectory(), uriAsFileName.Replace("//", "/")), - Path.GetFullPath(uriAsFileName)); - } - - [PlatformSpecific(TestPlatforms.Windows)] // Checks normalized long path (> MaxPath) on Windows - [Fact] - public static void GetFullPath_Windows_NormalizedLongPathTooLong() - { - // Try out a long path that normalizes down to more than MaxPath - string curDir = Directory.GetCurrentDirectory(); - const int Iters = 260; - var longPath = new StringBuilder(curDir, curDir.Length + (Iters * 4)); - for (int i = 0; i < Iters; i++) - { - longPath.Append(Path.DirectorySeparatorChar).Append('a').Append(Path.DirectorySeparatorChar).Append('.'); - } - - if (PathFeatures.AreAllLongPathsAvailable()) - { - // Now no longer throws unless over ~32K - Assert.NotNull(Path.GetFullPath(longPath.ToString())); - } - else - { - Assert.Throws(() => Path.GetFullPath(longPath.ToString())); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths - [Fact] - public static void GetFullPath_Windows_AlternateDataStreamsNotSupported() - { - // Throws via our invalid colon filtering - Assert.Throws(() => Path.GetFullPath(@"bad:path")); - Assert.Throws(() => Path.GetFullPath(@"C:\some\bad:path")); - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths - [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - [Theory] - [InlineData("http://www.microsoft.com")] - [InlineData("file://www.microsoft.com")] - public static void GetFullPath_Windows_URIFormatNotSupported_Desktop(string path) - { - // Throws via our invalid colon filtering - if (!PathFeatures.IsUsingLegacyPathNormalization()) - { - Assert.Throws(() => Path.GetFullPath(path)); - } - } - - [ActiveIssue(27269)] - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - [Theory] - [InlineData("http://www.microsoft.com")] - [InlineData("file://www.microsoft.com")] - public static void GetFullPath_Windows_URIFormatNotSupported_Core(string path) - { - // Throws via our invalid colon filtering - if (!PathFeatures.IsUsingLegacyPathNormalization()) - { - Assert.Throws(() => Path.GetFullPath(path)); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths - [Theory] - [InlineData(@"bad::$DATA")] - [InlineData(@"C :")] - [InlineData(@"C :\somedir")] - public static void GetFullPath_Windows_NotSupportedExceptionPaths(string path) - { - // Old path normalization throws ArgumentException, new one throws NotSupportedException - if (!PathFeatures.IsUsingLegacyPathNormalization()) - { - Assert.Throws(() => Path.GetFullPath(path)); - } - else - { - AssertExtensions.Throws(null, () => Path.GetFullPath(path)); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests legitimate Windows paths that are now allowed - [Theory] - [InlineData(@"C:...")] - [InlineData(@"C:...\somedir")] - [InlineData(@"\.. .\")] - [InlineData(@"\. .\")] - [InlineData(@"\ .\")] - public static void GetFullPath_Windows_LegacyArgumentExceptionPaths(string path) - { - if (PathFeatures.IsUsingLegacyPathNormalization()) - { - // We didn't allow these paths on < 4.6.2 - AssertExtensions.Throws(null, () => Path.GetFullPath(path)); - } - else - { - // These paths are legitimate Windows paths that can be created without extended syntax. - // We now allow them through. - Path.GetFullPath(path); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests MaxPathNotTooLong on Windows - [Fact] - public static void GetFullPath_Windows_MaxPathNotTooLong() - { - string value = @"C:\" + new string('a', 255) + @"\"; - if (PathFeatures.AreAllLongPathsAvailable()) - { - // Shouldn't throw anymore - Path.GetFullPath(value); - } - else - { - Assert.Throws(() => Path.GetFullPath(value)); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests PathTooLong on Windows - [Fact] - public static void GetFullPath_Windows_PathTooLong() - { - Assert.Throws(() => Path.GetFullPath(@"C:\" + new string('a', short.MaxValue) + @"\")); - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific paths - [Theory] - [InlineData(@"C:\", @"C:\")] - [InlineData(@"C:\.", @"C:\")] - [InlineData(@"C:\..", @"C:\")] - [InlineData(@"C:\..\..", @"C:\")] - [InlineData(@"C:\A\..", @"C:\")] - [InlineData(@"C:\..\..\A\..", @"C:\")] - public static void GetFullPath_Windows_RelativeRoot(string path, string expected) - { - Assert.Equal(Path.GetFullPath(path), expected); - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests legitimate strage windows paths that are now allowed - [Fact] - public static void GetFullPath_Windows_StrangeButLegalPaths() - { - // These are legal and creatable without using extended syntax if you use a trailing slash - // (such as "md ...\"). We used to filter these out, but now allow them to prevent apps from - // being blocked when they hit these paths. - string curDir = Directory.GetCurrentDirectory(); - if (PathFeatures.IsUsingLegacyPathNormalization()) - { - // Legacy path Path.GetFullePath() ignores . when there is less or more that two, when there is .. in the path it returns one directory up. - Assert.Equal( - Path.GetFullPath(curDir + Path.DirectorySeparatorChar), - Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ". " + Path.DirectorySeparatorChar)); - Assert.Equal( - Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar), - Path.GetFullPath(curDir + Path.DirectorySeparatorChar + "..." + Path.DirectorySeparatorChar)); - Assert.Equal( - Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar), - Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ".. " + Path.DirectorySeparatorChar)); - } - else - { - Assert.NotEqual( - Path.GetFullPath(curDir + Path.DirectorySeparatorChar), - Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ". " + Path.DirectorySeparatorChar)); - Assert.NotEqual( - Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar), - Path.GetFullPath(curDir + Path.DirectorySeparatorChar + "..." + Path.DirectorySeparatorChar)); - Assert.NotEqual( - Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar), - Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ".. " + Path.DirectorySeparatorChar)); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific paths - [Theory] - [InlineData(@"\\?\C:\ ")] - [InlineData(@"\\?\C:\ \ ")] - [InlineData(@"\\?\C:\ .")] - [InlineData(@"\\?\C:\ ..")] - [InlineData(@"\\?\C:\...")] - [InlineData(@"\\?\GLOBALROOT\")] - [InlineData(@"\\?\")] - [InlineData(@"\\?\.")] - [InlineData(@"\\?\..")] - [InlineData(@"\\?\\")] - [InlineData(@"\\?\C:\\")] - [InlineData(@"\\?\C:\|")] - [InlineData(@"\\?\C:\.")] - [InlineData(@"\\?\C:\..")] - [InlineData(@"\\?\C:\Foo1\.")] - [InlineData(@"\\?\C:\Foo2\..")] - [InlineData(@"\\?\UNC\")] - [InlineData(@"\\?\UNC\server1")] - [InlineData(@"\\?\UNC\server2\")] - [InlineData(@"\\?\UNC\server3\\")] - [InlineData(@"\\?\UNC\server4\..")] - [InlineData(@"\\?\UNC\server5\share\.")] - [InlineData(@"\\?\UNC\server6\share\..")] - [InlineData(@"\\?\UNC\a\b\\")] - [InlineData(@"\\.\")] - [InlineData(@"\\.\.")] - [InlineData(@"\\.\..")] - [InlineData(@"\\.\\")] - [InlineData(@"\\.\C:\\")] - [InlineData(@"\\.\C:\|")] - [InlineData(@"\\.\C:\.")] - [InlineData(@"\\.\C:\..")] - [InlineData(@"\\.\C:\Foo1\.")] - [InlineData(@"\\.\C:\Foo2\..")] - public static void GetFullPath_Windows_ValidExtendedPaths(string path) - { - if (PathFeatures.IsUsingLegacyPathNormalization()) - { - // Legacy Path doesn't support any of these paths. - AssertExtensions.ThrowsAny(() => Path.GetFullPath(path)); - return; - } - - // None of these should throw - if (path.StartsWith(@"\\?\")) - { - Assert.Equal(path, Path.GetFullPath(path)); - } - else - { - Path.GetFullPath(path); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific paths - [Theory] - [InlineData(@"\\.\UNC\")] - [InlineData(@"\\.\UNC\LOCALHOST")] - [InlineData(@"\\.\UNC\localHOST\")] - [InlineData(@"\\.\UNC\LOcaLHOST\\")] - [InlineData(@"\\.\UNC\lOCALHOST\..")] - [InlineData(@"\\.\UNC\LOCALhost\share\.")] - [InlineData(@"\\.\UNC\loCALHOST\share\..")] - [InlineData(@"\\.\UNC\a\b\\")] - public static void GetFullPath_Windows_ValidLegacy_ValidExtendedPaths(string path) - { - // should not throw - Path.GetFullPath(path); - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests valid paths based on UNC - [Theory] - // https://github.com/dotnet/corefx/issues/11965 - [InlineData(@"\\LOCALHOST\share\test.txt.~SS", @"\\LOCALHOST\share\test.txt.~SS")] - [InlineData(@"\\LOCALHOST\share1", @"\\LOCALHOST\share1")] - [InlineData(@"\\LOCALHOST\share3\dir", @"\\LOCALHOST\share3\dir")] - [InlineData(@"\\LOCALHOST\share4", @"\\LOCALHOST\share4\.")] - [InlineData(@"\\LOCALHOST\share5", @"\\LOCALHOST\share5\..")] - [InlineData(@"\\LOCALHOST\share6\", @"\\LOCALHOST\share6\ ")] - [InlineData(@"\\LOCALHOST\ share7\", @"\\LOCALHOST\ share7\")] - [InlineData(@"\\?\UNC\LOCALHOST\share8\test.txt.~SS", @"\\?\UNC\LOCALHOST\share8\test.txt.~SS")] - [InlineData(@"\\?\UNC\LOCALHOST\share9", @"\\?\UNC\LOCALHOST\share9")] - [InlineData(@"\\?\UNC\LOCALHOST\shareA\dir", @"\\?\UNC\LOCALHOST\shareA\dir")] - [InlineData(@"\\?\UNC\LOCALHOST\shareB\. ", @"\\?\UNC\LOCALHOST\shareB\. ")] - [InlineData(@"\\?\UNC\LOCALHOST\shareC\.. ", @"\\?\UNC\LOCALHOST\shareC\.. ")] - [InlineData(@"\\?\UNC\LOCALHOST\shareD\ ", @"\\?\UNC\LOCALHOST\shareD\ ")] - [InlineData(@"\\.\UNC\LOCALHOST\ shareE\", @"\\.\UNC\LOCALHOST\ shareE\")] - [InlineData(@"\\.\UNC\LOCALHOST\shareF\test.txt.~SS", @"\\.\UNC\LOCALHOST\shareF\test.txt.~SS")] - [InlineData(@"\\.\UNC\LOCALHOST\shareG", @"\\.\UNC\LOCALHOST\shareG")] - [InlineData(@"\\.\UNC\LOCALHOST\shareH\dir", @"\\.\UNC\LOCALHOST\shareH\dir")] - [InlineData(@"\\.\UNC\LOCALHOST\shareK\", @"\\.\UNC\LOCALHOST\shareK\ ")] - [InlineData(@"\\.\UNC\LOCALHOST\ shareL\", @"\\.\UNC\LOCALHOST\ shareL\")] - public static void GetFullPath_Windows_UNC_Valid(string expected, string input) - { - if (input.StartsWith(@"\\?\") && PathFeatures.IsUsingLegacyPathNormalization()) - { - AssertExtensions.Throws(null, () => Path.GetFullPath(input)); - } - else - { - Assert.Equal(expected, Path.GetFullPath(input)); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests valid paths based on UNC - [Theory] - [InlineData(@"\\.\UNC\LOCALHOST\shareI\", @"\\.\UNC\LOCALHOST\shareI", @"\\.\UNC\LOCALHOST\shareI\. ")] - [InlineData(@"\\.\UNC\LOCALHOST\shareJ\", @"\\.\UNC\LOCALHOST", @"\\.\UNC\LOCALHOST\shareJ\.. ")] - public static void GetFullPath_Windows_UNC_Valid_LegacyPathSupport(string normalExpected, string legacyExpected, string input) - { - string expected = PathFeatures.IsUsingLegacyPathNormalization() ? legacyExpected : normalExpected; - Assert.Equal(expected, Path.GetFullPath(input)); - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests invalid paths based on UNC - [Theory] - [InlineData(@"\\")] - [InlineData(@"\\LOCALHOST")] - [InlineData(@"\\LOCALHOST\")] - [InlineData(@"\\LOCALHOST\\")] - [InlineData(@"\\LOCALHOST\..")] - public static void GetFullPath_Windows_UNC_Invalid(string invalidPath) - { - AssertExtensions.Throws(null, () => Path.GetFullPath(invalidPath)); - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Uses P/Invokes to get short path name - public static void GetFullPath_Windows_83Paths() - { - // Create a temporary file name with a name longer than 8.3 such that it'll need to be shortened. - string tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".txt"); - File.Create(tempFilePath).Dispose(); - try - { - // Get its short name - var sb = new StringBuilder(260); - if (GetShortPathName(tempFilePath, sb, sb.Capacity) > 0) // only proceed if we could successfully create the short name - { - string shortName = sb.ToString(); - - // Make sure the shortened name expands back to the original one - // Sometimes shortening or GetFullPath is changing the casing of "temp" on some test machines: normalize both sides - tempFilePath = Regex.Replace(tempFilePath, @"\\temp\\", @"\TEMP\", RegexOptions.IgnoreCase); - shortName = Regex.Replace(Path.GetFullPath(shortName), @"\\temp\\", @"\TEMP\", RegexOptions.IgnoreCase); - Assert.Equal(tempFilePath, shortName); - - // Should work with device paths that aren't well-formed extended syntax - if (!PathFeatures.IsUsingLegacyPathNormalization()) - { - Assert.Equal(@"\\.\" + tempFilePath, Path.GetFullPath(@"\\.\" + shortName)); - Assert.Equal(@"\\?\" + tempFilePath, Path.GetFullPath(@"//?/" + shortName)); - - // Shouldn't mess with well-formed extended syntax - Assert.Equal(@"\\?\" + shortName, Path.GetFullPath(@"\\?\" + shortName)); - } - - // Validate case where short name doesn't expand to a real file - string invalidShortName = @"S:\DOESNT~1\USERNA~1.RED\LOCALS~1\Temp\bg3ylpzp"; - Assert.Equal(invalidShortName, Path.GetFullPath(invalidShortName)); - - // Same thing, but with a long path that normalizes down to a short enough one - const int Iters = 1000; - var shortLongName = new StringBuilder(invalidShortName, invalidShortName.Length + (Iters * 2)); - for (int i = 0; i < Iters; i++) - { - shortLongName.Append(Path.DirectorySeparatorChar).Append('.'); - } - Assert.Equal(invalidShortName, Path.GetFullPath(shortLongName.ToString())); - } - } - finally - { - File.Delete(tempFilePath); - } - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths - [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - [Theory] - [InlineData('*')] - [InlineData('?')] - public static void GetFullPath_Windows_Wildcards_Desktop(char wildcard) - { - AssertExtensions.Throws("path", null, () => Path.GetFullPath("test" + wildcard + "ing")); - } - - [ActiveIssue(27269)] - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - [Theory] - [InlineData('*')] - [InlineData('?')] - public static void GetFullPath_Windows_Wildcards_Core(char wildcard) - { - string path = "test" + wildcard + "ing"; - Assert.Equal(path, Path.GetFullPath(path)); - } - - // Windows-only P/Invoke to create 8.3 short names from long names - [DllImport("kernel32.dll", EntryPoint = "GetShortPathNameW" ,CharSet = CharSet.Unicode)] - private static extern uint GetShortPathName(string lpszLongPath, StringBuilder lpszShortPath, int cchBuffer); - - [Fact] - public static void InvalidPathChars_MatchesGetInvalidPathChars() - { -#pragma warning disable 0618 - Assert.NotNull(Path.InvalidPathChars); - Assert.Equal(Path.GetInvalidPathChars(), Path.InvalidPathChars); - Assert.Same(Path.InvalidPathChars, Path.InvalidPathChars); -#pragma warning restore 0618 - } } } diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.netcoreapp.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests.netcoreapp.cs index 4466d6a4d34f..67acf8f7a0d6 100644 --- a/src/System.Runtime.Extensions/tests/System/IO/PathTests.netcoreapp.cs +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests.netcoreapp.cs @@ -1,4 +1,3 @@ -// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,58 +6,84 @@ namespace System.IO.Tests { - public static partial class PathTests + public partial class PathTests : PathTestsBase { - [Theory, MemberData(nameof(GetDirectoryName_NonControl_Test_Data))] - public static void GetDirectoryName_NonControl_Span(string path) + [Fact] + public void GetDirectoryName_EmptyReturnsNull() + { + // In NetFX this throws argument exception + Assert.Null(Path.GetDirectoryName(string.Empty)); + } + + [Theory, MemberData(nameof(TestData_Spaces))] + public void GetDirectoryName_Spaces(string path) { - Assert.Equal(string.Empty, new string(Path.GetDirectoryName(path.AsSpan()))); + if (PlatformDetection.IsWindows) + { + // In Windows spaces are eaten by Win32, making them effectively empty + Assert.Null(Path.GetDirectoryName(path)); + } + else + { + Assert.Empty(Path.GetDirectoryName(path)); + } } - [Theory, MemberData(nameof(GetDirectoryName_NonControl_Test_Data))] - public static void GetDirectoryName_NonControlWithSeparator_Span(string path) + [Theory, MemberData(nameof(TestData_Spaces))] + public void GetDirectoryName_Span_Spaces(string path) { - Assert.Equal(path, new string(Path.GetDirectoryName(Path.Combine(path, path).AsSpan()))); + PathAssert.Empty(Path.GetDirectoryName(path.AsSpan())); } - [Theory, MemberData(nameof(GetDirectoryName_Test_Data))] - public static void GetDirectoryName_Span(string path, string expected) + [Theory, + MemberData(nameof(TestData_EmbeddedNull)), + MemberData(nameof(TestData_ControlChars)), + MemberData(nameof(TestData_UnicodeWhiteSpace))] + public void GetDirectoryName_NetFxInvalid(string path) { - if (path != null) - Assert.Equal(expected, new string(Path.GetDirectoryName(path.AsSpan()))); + Assert.Empty(Path.GetDirectoryName(path)); + Assert.Equal(path, Path.GetDirectoryName(Path.Combine(path, path))); + PathAssert.Empty(Path.GetDirectoryName(path.AsSpan())); + PathAssert.Equal(path, new string(Path.GetDirectoryName(Path.Combine(path, path).AsSpan()))); } - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific paths - [Theory, MemberData(nameof(GetDirectoryName_Windows_Test_Data))] - public static void GetDirectoryName_Windows_Span(string path, string expected) + [Theory, MemberData(nameof(TestData_GetDirectoryName))] + public void GetDirectoryName_Span(string path, string expected) { - Assert.Equal(expected ?? string.Empty, new string(Path.GetDirectoryName(path.AsSpan()))); + PathAssert.Equal(expected ?? ReadOnlySpan.Empty, Path.GetDirectoryName(path.AsSpan())); } [Fact] - public static void GetDirectoryName_CurrentDirectory_Span() + public void GetDirectoryName_Span_CurrentDirectory() { string curDir = Directory.GetCurrentDirectory(); - Assert.Equal(curDir, new string(Path.GetDirectoryName(Path.Combine(curDir, "baz").AsSpan()))); - Assert.Equal(string.Empty, new string(Path.GetDirectoryName(Path.GetPathRoot(curDir).AsSpan()))); + PathAssert.Equal(curDir, Path.GetDirectoryName(Path.Combine(curDir, "baz").AsSpan())); + PathAssert.Empty(Path.GetDirectoryName(Path.GetPathRoot(curDir).AsSpan())); } + [Theory, + InlineData(@" C:\dir/baz", @" C:\dir"), + InlineData(@" C:\dir/baz", @" C:\dir")] + public void GetDirectoryName_SkipSpaces(string path, string expected) + { + // We no longer trim leading spaces for any path + Assert.Equal(expected, Path.GetDirectoryName(path)); + } - - - [Theory, MemberData(nameof(GetExtension_Test_Data))] - public static void GetExtension_Span(string path, string expected) + [Theory, MemberData(nameof(TestData_GetExtension))] + public void GetExtension_Span(string path, string expected) { - if (path != null) - { - path = path.Replace('/', Path.DirectorySeparatorChar); + PathAssert.Equal(expected, Path.GetExtension(path.AsSpan())); + Assert.Equal(!string.IsNullOrEmpty(expected), Path.HasExtension(path.AsSpan())); + } - Assert.Equal(expected, new string(Path.GetExtension(path.AsSpan()))); - Assert.Equal(!string.IsNullOrEmpty(expected), Path.HasExtension(path.AsSpan())); - } + [Theory, MemberData(nameof(TestData_GetFileName))] + public void GetFileName_Span(string path, string expected) + { + PathAssert.Equal(expected, Path.GetFileName(path.AsSpan())); } - public static IEnumerable GetFileName_VolumeTestData() + public static IEnumerable TestData_GetFileName_Volume() { yield return new object[] { ":", ":" }; yield return new object[] { ".:", ".:" }; @@ -70,107 +95,78 @@ public static IEnumerable GetFileName_VolumeTestData() yield return new object[] { Path.Combine("bar", "baz", "file:exe"), "file:exe" }; } - [ActiveIssue(27269)] - [Theory] - [MemberData(nameof(GetFileName_VolumeTestData))] - public static void GetFileName_Volume(string path, string expected) + [Theory, MemberData(nameof(TestData_GetFileName_Volume))] + public void GetFileName_Volume(string path, string expected) { // We used to break on ':' on Windows. This is a valid file name character for alternate data streams. // Additionally the character can show up on unix volumes mounted to Windows. Assert.Equal(expected, Path.GetFileName(path)); - Assert.Equal(expected, new string(Path.GetFileName(path.AsSpan()))); - } - - [ActiveIssue(27269)] - [Theory] - [InlineData("B:", "")] - [InlineData("A:.", ".")] - [PlatformSpecific(TestPlatforms.Windows)] - public static void GetFileName_Volume_Windows(string path, string expected) - { - // With a valid drive letter followed by a colon, we have a root, but only on Windows. - Assert.Equal(expected, Path.GetFileName(path)); - Assert.Equal(expected, new string(Path.GetFileName(path.AsSpan()))); - } - - [Theory] - [InlineData("B:", "B:")] - [InlineData("A:.", "A:.")] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public static void GetFileName_Volume_Unix(string path, string expected) - { - // No such thing as a drive relative path on Unix. - Assert.Equal(expected, Path.GetFileName(path)); - Assert.Equal(expected, new string(Path.GetFileName(path.AsSpan()))); - } - - [Theory] - [MemberData(nameof(GetFileName_TestData))] - public static void GetFileName_Span(string path, string expected) - { - if (path != null) - Assert.Equal(expected, new string(Path.GetFileName(path.AsSpan()))); + PathAssert.Equal(expected, Path.GetFileName(path.AsSpan())); } - [Theory] - [MemberData(nameof(GetFileNameWithoutExtension_TestData))] - public static void GetFileNameWithoutExtension_Span(string path, string expected) + [ActiveIssue(27269, TestPlatforms.Windows)] + [Theory, MemberData(nameof(TestData_GetFileNameWithoutExtension))] + public void GetFileNameWithoutExtension_Span(string path, string expected) { - if(path != null) - Assert.Equal(expected, new string(Path.GetFileNameWithoutExtension(path.AsSpan()))); + PathAssert.Equal(expected, Path.GetFileNameWithoutExtension(path.AsSpan())); } [Fact] - public static void GetPathRoot_Empty_Span() - { - Assert.Equal(string.Empty, new string(Path.GetPathRoot(ReadOnlySpan.Empty))); - } - - [PlatformSpecific(TestPlatforms.Windows)] // Tests UNC - [Theory, MemberData(nameof(GetPathRoot_Windows_UncAndExtended_Test_Data))] - public static void GetPathRoot_Windows_UncAndExtended_Span(string value, string expected) - { - Assert.True(Path.IsPathRooted(value.AsSpan())); - Assert.Equal(expected, new string(Path.GetPathRoot(value.AsSpan()))); - } - - [PlatformSpecific(TestPlatforms.Windows)] - [Theory, MemberData(nameof(GetPathRoot_Windows_UncAndExtended_WithLegacySupport_Test_Data))] - public static void GetPathRoot_Windows_UncAndExtended_WithLegacySupport_Span(string normalExpected, string legacyExpected, string value) + public void GetPathRoot_Empty() { - Assert.True(Path.IsPathRooted(value.AsSpan())); - - string expected = PathFeatures.IsUsingLegacyPathNormalization() ? legacyExpected : normalExpected; - Assert.Equal(expected, new string(Path.GetPathRoot(value.AsSpan()))); + Assert.Null(Path.GetPathRoot(string.Empty)); } - [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific path convention - [Theory, MemberData(nameof(GetPathRoot_Windows_Test_Data))] - public static void GetPathRoot_Windows_Span(string value, string expected) + [Fact] + public void GetPathRoot_Empty_Span() { - Assert.True(Path.IsPathRooted(value.AsSpan())); - Assert.Equal(expected, new string(Path.GetPathRoot(value.AsSpan()))); + PathAssert.Empty(Path.GetPathRoot(ReadOnlySpan.Empty)); } - [Theory] - [InlineData("")] - [InlineData(" ")] - public static void IsPathRooted_Span(string path) + [Theory, + InlineData(nameof(TestData_Spaces)), + InlineData(nameof(TestData_ControlChars)), + InlineData(nameof(TestData_EmbeddedNull)), + InlineData(nameof(TestData_InvalidDriveLetters)), + InlineData(nameof(TestData_UnicodeWhiteSpace)), + InlineData(nameof(TestData_EmptyString))] + public void IsPathRooted_NegativeCases(string path) { + Assert.False(Path.IsPathRooted(path)); Assert.False(Path.IsPathRooted(path.AsSpan())); } - // Testing invalid drive letters !(a-zA-Z) - [PlatformSpecific(TestPlatforms.Windows)] - [Theory, MemberData(nameof(IsPathRooted_Windows_Invalid_Test_Data))] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Bug fixed on Core where it would return true if the first char is not a drive letter followed by a VolumeSeparatorChar coreclr/10297")] - public static void IsPathRooted_Windows_Invalid_Span(string value) + [ActiveIssue(27269, TestPlatforms.Windows)] + [Fact] + public void GetInvalidPathChars() { - Assert.False(Path.IsPathRooted(value.AsSpan())); + Assert.All(Path.GetInvalidPathChars(), c => + { + string bad = c.ToString(); + Assert.Equal(bad + ".ok", Path.ChangeExtension(bad, "ok")); + Assert.Equal(bad + Path.DirectorySeparatorChar + "ok", Path.Combine(bad, "ok")); + Assert.Equal("ok" + Path.DirectorySeparatorChar + "ok" + Path.DirectorySeparatorChar + bad, Path.Combine("ok", "ok", bad)); + Assert.Equal("ok" + Path.DirectorySeparatorChar + "ok" + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + "ok", Path.Combine("ok", "ok", bad, "ok")); + Assert.Equal(bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad, Path.Combine(bad, bad, bad, bad, bad)); + Assert.Equal("", Path.GetDirectoryName(bad)); + Assert.Equal(string.Empty, Path.GetExtension(bad)); + Assert.Equal(bad, Path.GetFileName(bad)); + Assert.Equal(bad, Path.GetFileNameWithoutExtension(bad)); + if (bad[0] == '\0') + { + Assert.Throws("path", () => Path.GetFullPath(bad)); + } + else + { + Assert.True(Path.GetFullPath(bad).EndsWith(bad)); + } + Assert.Equal(string.Empty, Path.GetPathRoot(bad)); + Assert.False(Path.IsPathRooted(bad)); + }); } [Fact] - public static void GetInvalidPathChars_Span() + public void GetInvalidPathChars_Span() { Assert.All(Path.GetInvalidPathChars(), c => { @@ -184,39 +180,42 @@ public static void GetInvalidPathChars_Span() }); } - [Theory, MemberData(nameof(GetDirectoryName_Unix_Test_Data))] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests Unix-specific paths - public static void GetDirectoryName_Unix_Span(string path, string expected) + [ActiveIssue(27269, TestPlatforms.Windows)] + [Theory, + InlineData("http://www.microsoft.com"), + InlineData("file://somefile")] + public void GetFullPath_URIsAsFileNames(string uriAsFileName) { - Assert.Equal(expected ?? string.Empty, new string(Path.GetDirectoryName(path.AsSpan()))); + // URIs are valid filenames, though the multiple slashes will be consolidated in GetFullPath + Assert.Equal( + Path.Combine(Directory.GetCurrentDirectory(), uriAsFileName.Replace("//", Path.DirectorySeparatorChar.ToString())), + Path.GetFullPath(uriAsFileName)); } - [PlatformSpecific(TestPlatforms.AnyUnix)] // Checks Unix-specific special characters in directory path - [Theory, MemberData(nameof(GetDirectoryName_ControlCharacters_Unix_Test_Data))] - public static void GetDirectoryName_ControlCharacters_Unix_Span(char ch, int count, string file) + [ActiveIssue(27269, TestPlatforms.Windows)] + [Theory, MemberData(nameof(TestData_NonDriveColonPaths))] + public void GetFullPath_NowSupportedColons(string path) { - Assert.Equal(new string(ch, count), new string(Path.GetDirectoryName(Path.Combine(new string(ch, count), file).AsSpan()))); + // Used to throw on Windows, now should never throw + Path.GetFullPath(path); } - [PlatformSpecific(TestPlatforms.AnyUnix)] // Checks file extension behavior on Unix - [Theory, MemberData(nameof(GetExtension_Unix_Test_Data))] - public static void GetExtension_Unix_Span(string path, string expected) + [ActiveIssue(27269, TestPlatforms.Windows)] + [Theory, MemberData(nameof(TestData_InvalidUnc))] + public static void GetFullPath_UNC_Invalid(string path) { - Assert.Equal(expected, new string(Path.GetExtension(path.AsSpan()))); - Assert.Equal(!string.IsNullOrEmpty(expected), Path.HasExtension(path.AsSpan())); + // These UNCs used to throw on Windows + Path.GetFullPath(path); } - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests Unix-specific valid file names - [Theory, MemberData(nameof(GetFileName_Unix_Test_Data))] - public static void GetFileName_Unix_Span(string file) - { - Assert.Equal(file, new string(Path.GetFileName(file).AsSpan())); - } - - [Fact] - public static void GetFileNameWithSpaces_Unix_Span() + [ActiveIssue(27269, TestPlatforms.Windows)] + [Theory, + MemberData(nameof(TestData_Wildcards)), + MemberData(nameof(TestData_ExtendedWildcards))] + public void GetFullPath_Wildcards(char wildcard) { - Assert.Equal("fi le", new string(Path.GetFileName(Path.Combine("b \r\n ar", "fi le").AsSpan()))); + string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + wildcard + "ing"); + Assert.Equal(path, Path.GetFullPath(path)); } } } diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTestsBase.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTestsBase.cs new file mode 100644 index 000000000000..5331d20f0e46 --- /dev/null +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTestsBase.cs @@ -0,0 +1,222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.IO.Tests +{ + public partial class PathTestsBase + { + public static TheoryData TestData_EmbeddedNull => new TheoryData + { + "a\0b" + }; + + public static TheoryData TestData_EmptyString => new TheoryData + { + "" + }; + + public static TheoryData TestData_ControlChars => new TheoryData + { + "\t", + "\r\n", + "\b", + "\v", + "\n" + }; + + public static TheoryData TestData_NonDriveColonPaths => new TheoryData + { + @"bad:path", + @"C:\some\bad:path", + @"http://www.microsoft.com", + @"file://www.microsoft.com", + @"bad::$DATA", + @"C :", + @"C :\somedir" + }; + + public static TheoryData TestData_Spaces => new TheoryData + { + " ", + " " + }; + + public static TheoryData TestData_Periods => new TheoryData + { + // One and two periods have special meaning (current and parent dir) + "...", + "...." + }; + + public static TheoryData TestData_Wildcards => new TheoryData + { + "*", + "?" + }; + + public static TheoryData TestData_ExtendedWildcards => new TheoryData + { + // These are supported by Windows although .NET blocked them historically + "\"", + "<", + ">" + }; + + public static TheoryData TestData_UnicodeWhiteSpace => new TheoryData + { + "\u00A0", // Non-breaking Space + "\u2028", // Line separator + "\u2029", // Paragraph separator + }; + + public static TheoryData TestData_InvalidUnc => new TheoryData + { + // .NET used to validate properly formed UNCs + @"\\", + @"\\LOCALHOST", + @"\\LOCALHOST\", + @"\\LOCALHOST\\", + @"\\LOCALHOST\.." + }; + + public static TheoryData TestData_InvalidDriveLetters => new TheoryData + { + { @"@:\foo" }, // 064 = @ 065 = A + { @"[:\\" }, // 091 = [ 090 = Z + { @"`:\foo "}, // 096 = ` 097 = a + { @"{:\\" }, // 123 = { 122 = z + { @"@:/foo" }, + { @"[://" }, + { @"`:/foo "}, + { @"{:/" }, + { @"]:" } + }; + + public static TheoryData TestData_ValidDriveLetters => new TheoryData + { + { @"A:\foo" }, // 064 = @ 065 = A + { @"Z:\\" }, // 091 = [ 090 = Z + { @"a:\foo "}, // 096 = ` 097 = a + { @"z:\\" }, // 123 = { 122 = z + { @"B:/foo" }, + { @"D://" }, + { @"E:/foo "}, + { @"F:/" }, + { @"G:" } + }; + + public static TheoryData TestData_GetDirectoryName => new TheoryData + { + { ".", "" }, + { "..", "" }, + { "baz", "" }, + { Path.Combine("dir", "baz"), "dir" }, + { "dir.foo" + Path.AltDirectorySeparatorChar + "baz.txt", "dir.foo" }, + { Path.Combine("dir", "baz", "bar"), Path.Combine("dir", "baz") }, + { Path.Combine("..", "..", "files.txt"), Path.Combine("..", "..") }, + { Path.DirectorySeparatorChar + "foo", Path.DirectorySeparatorChar.ToString() }, + { Path.DirectorySeparatorChar.ToString(), null } + }; + + public static TheoryData TestData_GetDirectoryName_Windows => new TheoryData + { + { @"C:\", null }, + { @"C:/", null }, + { @"C:", null }, + { @"dir\\baz", "dir" }, + { @"dir//baz", "dir" }, + { @"C:\foo", @"C:\" }, + { @"C:foo", "C:" } + }; + + public static TheoryData TestData_GetExtension => new TheoryData + { + { @"file.exe", ".exe" }, + { @"file", "" }, + { @"file.", "" }, + { @"file.s", ".s" }, + { @"test/file", "" }, + { @"test/file.extension", ".extension" }, + { @"test\file", "" }, + { @"test\file.extension", ".extension" }, + { "file.e xe", ".e xe"}, + { "file. ", ". "}, + { " file. ", ". "}, + { " file.extension", ".extension"} + }; + + public static TheoryData TestData_GetFileName => new TheoryData + { + { ".", "." }, + { "..", ".." }, + { "file", "file" }, + { "file.", "file." }, + { "file.exe", "file.exe" }, + { " . ", " . " }, + { " .. ", " .. " }, + { "fi le", "fi le" }, + { Path.Combine("baz", "file.exe"), "file.exe" }, + { Path.Combine("baz", "file.exe") + Path.AltDirectorySeparatorChar, "" }, + { Path.Combine("bar", "baz", "file.exe"), "file.exe" }, + { Path.Combine("bar", "baz", "file.exe") + Path.DirectorySeparatorChar, "" } + }; + + public static TheoryData TestData_GetFileNameWithoutExtension => new TheoryData + { + { "", "" }, + { "file", "file" }, + { "file.exe", "file" }, + { Path.Combine("bar", "baz", "file.exe"), "file" }, + { Path.Combine("bar", "baz") + Path.DirectorySeparatorChar, "" } + }; + + public static TheoryData TestData_GetPathRoot_Unc => new TheoryData + { + { @"\\test\unc\path\to\something", @"\\test\unc" }, + { @"\\a\b\c\d\e", @"\\a\b" }, + { @"\\a\b\", @"\\a\b" }, + { @"\\a\b", @"\\a\b" }, + { @"\\test\unc", @"\\test\unc" }, + }; + + // TODO: Include \\.\ as well + public static TheoryData TestData_GetPathRoot_DevicePaths => new TheoryData + { + { @"\\?\UNC\test\unc\path\to\something", PathFeatures.IsUsingLegacyPathNormalization() ? @"\\?\UNC" : @"\\?\UNC\test\unc" }, + { @"\\?\UNC\test\unc", PathFeatures.IsUsingLegacyPathNormalization() ? @"\\?\UNC" : @"\\?\UNC\test\unc" }, + { @"\\?\UNC\a\b1", PathFeatures.IsUsingLegacyPathNormalization() ? @"\\?\UNC" : @"\\?\UNC\a\b1" }, + { @"\\?\UNC\a\b2\", PathFeatures.IsUsingLegacyPathNormalization() ? @"\\?\UNC" : @"\\?\UNC\a\b2" }, + { @"\\?\C:\foo\bar.txt", PathFeatures.IsUsingLegacyPathNormalization() ? @"\\?\C:" : @"\\?\C:\" } + }; + + public static TheoryData TestData_GetPathRoot_Windows => new TheoryData + { + { @"C:", @"C:" }, + { @"C:\", @"C:\" }, + { @"C:\\", @"C:\" }, + { @"C:\foo1", @"C:\" }, + { @"C:\\foo2", @"C:\" }, + }; + + protected static void GetTempPath_SetEnvVar(string envVar, string expected, string newTempPath) + { + string original = Path.GetTempPath(); + Assert.NotNull(original); + try + { + Environment.SetEnvironmentVariable(envVar, newTempPath); + Assert.Equal( + Path.GetFullPath(expected), + Path.GetFullPath(Path.GetTempPath())); + } + finally + { + Environment.SetEnvironmentVariable(envVar, original); + Assert.Equal(original, Path.GetTempPath()); + } + } + } +} diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTestsBase.netcoreapp.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTestsBase.netcoreapp.cs new file mode 100644 index 000000000000..ce9610c79e84 --- /dev/null +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTestsBase.netcoreapp.cs @@ -0,0 +1,30 @@ +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class PathTestsBase + { + protected static class PathAssert + { + public static void Equal(ReadOnlySpan expected, ReadOnlySpan actual) + { + if (!actual.SequenceEqual(expected)) + throw new Xunit.Sdk.EqualException(new string(expected), new string(actual)); + } + + public static void Empty(ReadOnlySpan actual) + { + if (actual.Length > 0) + throw new Xunit.Sdk.NotEmptyException(); + } + } + } +} diff --git a/src/System.Runtime.Extensions/tests/TestHelpers.cs b/src/System.Runtime.Extensions/tests/TestHelpers.cs new file mode 100644 index 000000000000..c3cfb125a75c --- /dev/null +++ b/src/System.Runtime.Extensions/tests/TestHelpers.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System +{ + public static class TestHelpers + { + + public static TheoryData Concat(this IEnumerable first, TheoryData second) + { + TheoryData data = new TheoryData(); + foreach (var item in first) + data.Add(item); + foreach (var item in second) + data.Add((T)item[0]); + return data; + } + + public static TheoryData Concat(this TheoryData first, TheoryData second) + { + TheoryData data = new TheoryData(); + foreach (var item in first) + data.Add((T)item[0]); + foreach (var item in second) + data.Add((T)item[0]); + return data; + } + } +} From 276c992e4b1a24243b4d0591221bd4af343588ca Mon Sep 17 00:00:00 2001 From: Jeremy Kuhne Date: Sun, 25 Feb 2018 10:39:38 -0800 Subject: [PATCH 2/3] Address feedback --- .../tests/System.Runtime.Extensions.Tests.csproj | 8 ++++---- .../tests/System/IO/PathTests.netcoreapp.cs | 1 + .../System/IO/{PathTests.Unix.cs => PathTests_Unix.cs} | 0 .../IO/{PathTests.Windows.cs => PathTests_Windows.cs} | 0 ...dows.netcoreapp.cs => PathTests_Windows.netcoreapp.cs} | 0 ...hTests.Windows.netfx.cs => PathTests_Windows.netfx.cs} | 5 ----- src/System.Runtime.Extensions/tests/TestHelpers.cs | 5 ----- 7 files changed, 5 insertions(+), 14 deletions(-) rename src/System.Runtime.Extensions/tests/System/IO/{PathTests.Unix.cs => PathTests_Unix.cs} (100%) rename src/System.Runtime.Extensions/tests/System/IO/{PathTests.Windows.cs => PathTests_Windows.cs} (100%) rename src/System.Runtime.Extensions/tests/System/IO/{PathTests.Windows.netcoreapp.cs => PathTests_Windows.netcoreapp.cs} (100%) rename src/System.Runtime.Extensions/tests/System/IO/{PathTests.Windows.netfx.cs => PathTests_Windows.netfx.cs} (97%) diff --git a/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj b/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj index a8068ffed913..2d8d8182dda8 100644 --- a/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj +++ b/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj @@ -23,8 +23,8 @@ - - + + @@ -39,9 +39,9 @@ - + - + diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.netcoreapp.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests.netcoreapp.cs index 67acf8f7a0d6..205323dc0227 100644 --- a/src/System.Runtime.Extensions/tests/System/IO/PathTests.netcoreapp.cs +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests.netcoreapp.cs @@ -1,3 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Unix.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests_Unix.cs similarity index 100% rename from src/System.Runtime.Extensions/tests/System/IO/PathTests.Unix.cs rename to src/System.Runtime.Extensions/tests/System/IO/PathTests_Unix.cs diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.cs similarity index 100% rename from src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.cs rename to src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.cs diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netcoreapp.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.netcoreapp.cs similarity index 100% rename from src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netcoreapp.cs rename to src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.netcoreapp.cs diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netfx.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.netfx.cs similarity index 97% rename from src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netfx.cs rename to src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.netfx.cs index 8fb382e3520c..06de4943891a 100644 --- a/src/System.Runtime.Extensions/tests/System/IO/PathTests.Windows.netfx.cs +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.netfx.cs @@ -2,11 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Xunit; namespace System.IO.Tests diff --git a/src/System.Runtime.Extensions/tests/TestHelpers.cs b/src/System.Runtime.Extensions/tests/TestHelpers.cs index c3cfb125a75c..88d77f624073 100644 --- a/src/System.Runtime.Extensions/tests/TestHelpers.cs +++ b/src/System.Runtime.Extensions/tests/TestHelpers.cs @@ -2,18 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Xunit; namespace System { public static class TestHelpers { - public static TheoryData Concat(this IEnumerable first, TheoryData second) { TheoryData data = new TheoryData(); From cc62fc00c84ed0a4d889c31fba7c3d98b70f826f Mon Sep 17 00:00:00 2001 From: Jeremy Kuhne Date: Sun, 25 Feb 2018 11:06:19 -0800 Subject: [PATCH 3/3] More name tweaks --- .../tests/System.Runtime.Extensions.Tests.csproj | 4 ++-- .../tests/System/IO/{Path.Combine.cs => PathTests_Combine.cs} | 0 ...{PathTests_Windows.netfx.cs => PathTests_Windows_NetFX.cs} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/System.Runtime.Extensions/tests/System/IO/{Path.Combine.cs => PathTests_Combine.cs} (100%) rename src/System.Runtime.Extensions/tests/System/IO/{PathTests_Windows.netfx.cs => PathTests_Windows_NetFX.cs} (98%) diff --git a/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj b/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj index 2d8d8182dda8..59eaf96f2212 100644 --- a/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj +++ b/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj @@ -23,7 +23,7 @@ - + @@ -61,7 +61,7 @@ - + diff --git a/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests_Combine.cs similarity index 100% rename from src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs rename to src/System.Runtime.Extensions/tests/System/IO/PathTests_Combine.cs diff --git a/src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.netfx.cs b/src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows_NetFX.cs similarity index 98% rename from src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.netfx.cs rename to src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows_NetFX.cs index 06de4943891a..56dee5db9db3 100644 --- a/src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows.netfx.cs +++ b/src/System.Runtime.Extensions/tests/System/IO/PathTests_Windows_NetFX.cs @@ -8,7 +8,7 @@ namespace System.IO.Tests { [PlatformSpecific(TestPlatforms.Windows)] [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - public class PathTests_NetFX : PathTestsBase + public class PathTests_Windows_NetFX : PathTestsBase { [Theory, MemberData(nameof(TestData_EmbeddedNull)),