|
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.UnitTests
{
public class FileUtilitiesTests
{
[ConditionalFact(typeof(WindowsOnly))]
public void IsAbsolute()
{
Assert.False(PathUtilities.IsAbsolute(null));
Assert.False(PathUtilities.IsAbsolute(""));
Assert.False(PathUtilities.IsAbsolute("C"));
Assert.False(PathUtilities.IsAbsolute("C:"));
Assert.True(PathUtilities.IsAbsolute(@"C:\"));
Assert.True(PathUtilities.IsAbsolute(@"C:/"));
Assert.True(PathUtilities.IsAbsolute(@"C:\\"));
Assert.False(PathUtilities.IsAbsolute(@"C\"));
Assert.True(PathUtilities.IsAbsolute(@"\\")); // incomplete UNC
Assert.True(PathUtilities.IsAbsolute(@"\\S")); // incomplete UNC
Assert.True(PathUtilities.IsAbsolute(@"\/C")); // incomplete UNC
Assert.True(PathUtilities.IsAbsolute(@"\/C\")); // incomplete UNC
Assert.True(PathUtilities.IsAbsolute(@"\\server")); // incomplete UNC
Assert.True(PathUtilities.IsAbsolute(@"\\server\share")); // UNC
Assert.True(PathUtilities.IsAbsolute(@"\\?\C:\share")); // long UNC
Assert.False(PathUtilities.IsAbsolute(@"\C"));
Assert.False(PathUtilities.IsAbsolute(@"/C"));
}
[ConditionalFact(typeof(WindowsOnly))]
public void GetPathRoot()
{
Assert.Null(PathUtilities.GetPathRoot(null));
Assert.Equal("", PathUtilities.GetPathRoot(""));
Assert.Equal("", PathUtilities.GetPathRoot("C"));
Assert.Equal("", PathUtilities.GetPathRoot("abc.txt"));
Assert.Equal("C:", PathUtilities.GetPathRoot("C:"));
Assert.Equal(@"C:\", PathUtilities.GetPathRoot(@"C:\"));
Assert.Equal(@"C:/", PathUtilities.GetPathRoot(@"C:/"));
Assert.Equal(@"C:\", PathUtilities.GetPathRoot(@"C:\\"));
Assert.Equal(@"C:/", PathUtilities.GetPathRoot(@"C:/\"));
Assert.Equal(@"*:/", PathUtilities.GetPathRoot(@"*:/"));
Assert.Equal(@"0:/", PathUtilities.GetPathRoot(@"0:/"));
Assert.Equal(@"::/", PathUtilities.GetPathRoot(@"::/"));
// '/' is an absolute path on unix-like systems
switch (Environment.OSVersion.Platform)
{
case PlatformID.MacOSX:
case PlatformID.Unix:
Assert.Equal("/", PathUtilities.GetPathRoot(@"/"));
Assert.Equal(@"/", PathUtilities.GetPathRoot(@"/x"));
// Be permissive of either directory separator, just
// like we are in other cases
Assert.Equal(@"\", PathUtilities.GetPathRoot(@"\"));
Assert.Equal(@"\", PathUtilities.GetPathRoot(@"\x"));
break;
default:
Assert.Equal(@"\", PathUtilities.GetPathRoot(@"\"));
Assert.Equal(@"\", PathUtilities.GetPathRoot(@"\x"));
break;
}
Assert.Equal(@"\\", PathUtilities.GetPathRoot(@"\\"));
Assert.Equal(@"\\x", PathUtilities.GetPathRoot(@"\\x"));
Assert.Equal(@"\\x\", PathUtilities.GetPathRoot(@"\\x\"));
Assert.Equal(@"\\x\y", PathUtilities.GetPathRoot(@"\\x\y"));
Assert.Equal(@"\\\x\y", PathUtilities.GetPathRoot(@"\\\x\y"));
Assert.Equal(@"\\\\x\y", PathUtilities.GetPathRoot(@"\\\\x\y"));
Assert.Equal(@"\\x\\y", PathUtilities.GetPathRoot(@"\\x\\y"));
Assert.Equal(@"\\/x\\/y", PathUtilities.GetPathRoot(@"\\/x\\/y"));
Assert.Equal(@"\\/x\\/y", PathUtilities.GetPathRoot(@"\\/x\\/y/"));
Assert.Equal(@"\\/x\\/y", PathUtilities.GetPathRoot(@"\\/x\\/y\/"));
Assert.Equal(@"\\/x\\/y", PathUtilities.GetPathRoot(@"\\/x\\/y\/zzz"));
Assert.Equal(@"\\x\y", PathUtilities.GetPathRoot(@"\\x\y"));
Assert.Equal(@"\\x\y", PathUtilities.GetPathRoot(@"\\x\y\\"));
Assert.Equal(@"\\abc\xyz", PathUtilities.GetPathRoot(@"\\abc\xyz"));
Assert.Equal(@"\\server\$c", PathUtilities.GetPathRoot(@"\\server\$c\Public"));
// TODO (tomat): long UNC paths
// Assert.Equal(@"\\?\C:\", PathUtilities.GetPathRoot(@"\\?\C:\abc\def"));
}
[ConditionalFact(typeof(WindowsOnly))]
public void CombinePaths()
{
Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @""));
Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", null));
Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", null));
Assert.Null(PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"C:\goo"));
Assert.Null(PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"C:goo"));
Assert.Null(PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"\goo"));
Assert.Equal(@"C:\x\y\goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x\y", @"goo"));
Assert.Equal(@"C:\x/y\goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"goo"));
Assert.Equal(@"C:\x/y\.\goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @".\goo"));
Assert.Equal(@"C:\x/y\./goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"./goo"));
Assert.Equal(@"C:\x/y\..\goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"..\goo"));
Assert.Equal(@"C:\x/y\../goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"../goo"));
}
[ConditionalFact(typeof(WindowsOnly))]
public void ResolveRelativePath()
{
string baseDir = @"X:\rootdir\dir";
string[] noSearchPaths = new string[0];
// absolute path:
TestPath(@"C:\abc\def.dll", @"Q:\baz\x.csx", baseDir, noSearchPaths, @"C:\abc\def.dll");
TestPath(@"C:\abc\\\\\def.dll", @"Q:\baz\x.csx", baseDir, noSearchPaths, @"C:\abc\\\\\def.dll");
// root-relative path:
TestPath(@"\abc\def.dll", @"Q:\baz\x.csx", baseDir, noSearchPaths, @"Q:\abc\def.dll");
TestPath(@"\abc\def.dll", null, baseDir, noSearchPaths, @"X:\abc\def.dll");
TestPath(@"\abc\def.dll", "goo.csx", null, noSearchPaths, null);
// TestPath(@"\abc\def.dll", @"C:goo.csx", null, noSearchPaths, null);
// TestPath(@"/abc\def.dll", @"\goo.csx", null, noSearchPaths, null);
TestPath(@"/abc\def.dll", null, @"\\x\y\z", noSearchPaths, @"\\x\y\abc\def.dll");
TestPath(@"/abc\def.dll", null, null, noSearchPaths, null);
TestPath(@"/**/", null, baseDir, noSearchPaths, @"X:\**/");
TestPath(@"/a/z.txt", null, @"?:\*\<>", noSearchPaths, @"?:\a/z.txt");
// UNC path:
TestPath(@"\abc\def.dll", @"\\mymachine\root\x.csx", baseDir, noSearchPaths, @"\\mymachine\root\abc\def.dll");
TestPath(@"\abc\def.dll", null, @"\\mymachine\root\x.csx", noSearchPaths, @"\\mymachine\root\abc\def.dll");
TestPath(@"\\abc\def\baz.dll", null, @"\\mymachine\root\x.csx", noSearchPaths, @"\\abc\def\baz.dll");
// incomplete UNC paths (considered absolute and returned as they are):
TestPath(@"\\", null, @"\\mymachine\root\x.csx", noSearchPaths, @"\\");
TestPath(@"\\goo", null, @"\\mymachine\root\x.csx", noSearchPaths, @"\\goo");
// long UNC path:
// TODO (tomat):
// Doesn't work since "?" in paths is not handled by BCL
// TestPath(resolver, @"\abc\def.dll", @"\\?\C:\zzz\x.csx", @"\\?\C:\abc\def.dll");
TestPath(@"./def.dll", @"Q:\abc\x.csx", baseDir, noSearchPaths, @"Q:\abc\./def.dll");
TestPath(@"./def.dll", @"Q:\abc\x.csx", baseDir, noSearchPaths, @"Q:\abc\./def.dll");
TestPath(@".", @"Q:\goo\x.csx", baseDir, noSearchPaths, @"Q:\goo");
TestPath(@"..", @"Q:\goo\x.csx", baseDir, noSearchPaths, @"Q:\goo\.."); // doesn't normalize
TestPath(@".\", @"Q:\goo\x.csx", baseDir, noSearchPaths, @"Q:\goo\.\");
TestPath(@"..\", @"Q:\goo\x.csx", baseDir, noSearchPaths, @"Q:\goo\..\"); // doesn't normalize
// relative base paths:
TestPath(@".\y.dll", @"x.csx", baseDir, noSearchPaths, @"X:\rootdir\dir\.\y.dll");
TestPath(@".\y.dll", @"goo\x.csx", baseDir, noSearchPaths, @"X:\rootdir\dir\goo\.\y.dll");
TestPath(@".\y.dll", @".\goo\x.csx", baseDir, noSearchPaths, @"X:\rootdir\dir\.\goo\.\y.dll");
TestPath(@".\y.dll", @"..\x.csx", baseDir, noSearchPaths, @"X:\rootdir\dir\..\.\y.dll"); // doesn't normalize
TestPath(@".\\y.dll", @"..\x.csx", baseDir, noSearchPaths, @"X:\rootdir\dir\..\.\\y.dll"); // doesn't normalize
TestPath(@".\/y.dll", @"..\x.csx", baseDir, noSearchPaths, @"X:\rootdir\dir\..\.\/y.dll"); // doesn't normalize
TestPath(@"..\y.dll", @"..\x.csx", baseDir, noSearchPaths, @"X:\rootdir\dir\..\..\y.dll"); // doesn't normalize
// unqualified relative path, look in base directory:
TestPath(@"y.dll", @"x.csx", baseDir, noSearchPaths, @"X:\rootdir\dir\y.dll");
TestPath(@"y.dll", @"x.csx", baseDir, new[] { @"Z:\" }, @"X:\rootdir\dir\y.dll");
// drive-relative path (not supported -> null)
TestPath(@"C:y.dll", @"x.csx", baseDir, noSearchPaths, null);
TestPath("C:\tools\\", null, @"d:\z", noSearchPaths, null);
// invalid paths
Assert.Equal(PathKind.RelativeToCurrentRoot, PathUtilities.GetPathKind(@"/c:x.dll"));
TestPath(@"/c:x.dll", null, @"d:\", noSearchPaths, @"d:\c:x.dll");
Assert.Equal(PathKind.RelativeToCurrentRoot, PathUtilities.GetPathKind(@"/:x.dll"));
TestPath(@"/:x.dll", null, @"d:\", noSearchPaths, @"d:\:x.dll");
Assert.Equal(PathKind.Absolute, PathUtilities.GetPathKind(@"//:x.dll"));
TestPath(@"//:x.dll", null, @"d:\", noSearchPaths, @"//:x.dll");
Assert.Equal(PathKind.RelativeToDriveDirectory, PathUtilities.GetPathKind(@"c::x.dll"));
TestPath(@"c::x.dll", null, @"d:\", noSearchPaths, null);
Assert.Equal(PathKind.RelativeToCurrentDirectory, PathUtilities.GetPathKind(@".\:x.dll"));
TestPath(@".\:x.dll", null, @"d:\z", noSearchPaths, @"d:\z\.\:x.dll");
Assert.Equal(PathKind.RelativeToCurrentParent, PathUtilities.GetPathKind(@"..\:x.dll"));
TestPath(@"..\:x.dll", null, @"d:\z", noSearchPaths, @"d:\z\..\:x.dll");
// empty paths
Assert.Equal(PathKind.Empty, PathUtilities.GetPathKind(@""));
TestPath(@"", @"c:\temp", @"d:\z", noSearchPaths, null);
Assert.Equal(PathKind.Empty, PathUtilities.GetPathKind(" \t\r\n "));
TestPath(" \t\r\n ", @"c:\temp", @"d:\z", noSearchPaths, null);
}
private void TestPath(string path, string basePath, string baseDirectory, IEnumerable<String> searchPaths, string expected)
{
string actual = FileUtilities.ResolveRelativePath(path, basePath, baseDirectory, searchPaths, p => true);
Assert.Equal(expected, actual, StringComparer.OrdinalIgnoreCase);
}
private void TestGetExtension(string path, string expected)
{
Assert.Equal(expected, PathUtilities.GetExtension(path));
Assert.Equal(expected, Path.GetExtension(path));
}
private void TestRemoveExtension(string path, string expected)
{
Assert.Equal(expected, PathUtilities.RemoveExtension(path));
Assert.Equal(expected, Path.GetFileNameWithoutExtension(path));
}
private void TestChangeExtension(string path, string extension, string expected)
{
Assert.Equal(expected, PathUtilities.ChangeExtension(path, extension));
Assert.Equal(expected, Path.ChangeExtension(path, extension));
}
[ConditionalFact(typeof(WindowsOnly))]
public void Extension()
{
TestGetExtension(path: "a.dll", expected: ".dll");
TestGetExtension(path: "a.exe.config", expected: ".config");
TestGetExtension(path: ".goo", expected: ".goo");
TestGetExtension(path: ".goo.dll", expected: ".dll");
TestGetExtension(path: "goo", expected: "");
TestGetExtension(path: "goo.", expected: "");
TestGetExtension(path: "goo..", expected: "");
TestGetExtension(path: "goo...", expected: "");
Assert.Equal(".dll", PathUtilities.GetExtension("*.dll"));
TestRemoveExtension(path: "a.dll", expected: "a");
TestRemoveExtension(path: "a.exe.config", expected: "a.exe");
TestRemoveExtension(path: ".goo", expected: "");
TestRemoveExtension(path: ".goo.dll", expected: ".goo");
TestRemoveExtension(path: "goo", expected: "goo");
TestRemoveExtension(path: "goo.", expected: "goo");
TestRemoveExtension(path: "goo..", expected: "goo.");
TestRemoveExtension(path: "goo...", expected: "goo..");
Assert.Equal("*", PathUtilities.RemoveExtension("*.dll"));
TestChangeExtension(path: "a.dll", extension: ".exe", expected: "a.exe");
TestChangeExtension(path: "a.dll", extension: "exe", expected: "a.exe");
TestChangeExtension(path: "a.dll", extension: "", expected: "a.");
TestChangeExtension(path: "a.dll", extension: ".", expected: "a.");
TestChangeExtension(path: "a.dll", extension: "..", expected: "a..");
TestChangeExtension(path: "a.dll", extension: "...", expected: "a...");
TestChangeExtension(path: "a.dll", extension: " ", expected: "a. ");
TestChangeExtension(path: "a", extension: ".exe", expected: "a.exe");
TestChangeExtension(path: "a.", extension: "exe", expected: "a.exe");
TestChangeExtension(path: "a..", extension: "exe", expected: "a..exe");
TestChangeExtension(path: "a.", extension: "e.x.e", expected: "a.e.x.e");
TestChangeExtension(path: ".", extension: "", expected: ".");
TestChangeExtension(path: "..", extension: ".", expected: "..");
TestChangeExtension(path: "..", extension: "..", expected: "...");
TestChangeExtension(path: "", extension: "", expected: "");
TestChangeExtension(path: null, extension: "", expected: null);
TestChangeExtension(path: null, extension: null, expected: null);
Assert.Equal("*", PathUtilities.RemoveExtension("*.dll"));
}
[ConditionalTheory(typeof(WindowsOnly))]
[InlineData(@"x:\a\b\file.cs", "x:/a/b/file.cs")]
[InlineData(@"x:/a/b/file.cs", "x:/a/b/file.cs")]
[InlineData(@"x:\a\b\", "x:/a/b/")]
[InlineData(@"x:\a\b", "x:/a/b")]
[InlineData(@"x:\a\..\b\file.cs", "x:/b/file.cs")]
[InlineData(@"x:/a/../b/file.cs", "x:/b/file.cs")]
[InlineData(@"x:\a\b\..\c\", "x:/a/c/")]
[InlineData(@"x:\a\.\.\b\..\c\", "x:/a/c/")]
[InlineData(@"x:\..\..\a\.\.\b\..\c\", "x:/a/c/")]
[InlineData(@"\\server\a\b\", @"\\server\a\b\")]
[InlineData(@"\\server\..\a\.\.\b\..\c\", @"\\server\..\a\.\.\b\..\c\")]
[InlineData(@"x:a.cs", "x:a.cs")]
[InlineData(@"x:a//b//..//a.cs", "x:a//b//..//a.cs")]
[InlineData(@"ab:\c\d\", @"ab:\c\d\")]
[InlineData(@"ab:/c/d", @"ab:/c/d")]
[InlineData(@"ab:\c\..\d\", @"ab:\c\..\d\")]
[InlineData(@"\\.\C:\a\b.cs", @"\\.\C:\a\b.cs")]
[InlineData(@"\\.\C:\a\..\b.cs", @"\\.\C:\a\..\b.cs")]
[InlineData(@"\\?\C:\a\b.cs", @"\\?\C:\a\b.cs")]
[InlineData(@"\\.\Volume{00000000-0000-0000-0000-000000000000}\a\b.cs", @"\\.\Volume{00000000-0000-0000-0000-000000000000}\a\b.cs")]
[InlineData(@"\", @"\")]
[InlineData(@"\\", @"\\")]
[InlineData(@"\\server", @"\\server")]
[InlineData(@"x:", "x:")]
[InlineData(@"x:\", @"x:/")]
public void TestExpandAbsolutePathWithRelativeParts_Windows(string input, string expected) => TestExpandAbsolutePathWithRelativeParts(input, expected);
[Theory]
[InlineData("/", "/")]
[InlineData("/a/b/c", "/a/b/c")]
[InlineData("/a/b/c/file.cs", "/a/b/c/file.cs")]
[InlineData("/a/b/../c/file.cs", "/a/c/file.cs")]
[InlineData("/a/../b/../c/file.cs", "/c/file.cs")]
[InlineData("/../../c/file.cs", "/c/file.cs")]
[InlineData("/a/../b/./././../c/file.cs", "/c/file.cs")]
// not absolute paths
[InlineData("a/b/c", "a/b/c")]
[InlineData("./a/b/c", "./a/b/c")]
[InlineData("./a/../b/c", "./a/../b/c")]
[InlineData("../a/../b/c", "../a/../b/c")]
[InlineData("foo", "foo")]
[InlineData("", "")]
[InlineData(".", ".")]
[InlineData("..", "..")]
[InlineData("../", "../")]
[InlineData("./", "./")]
public void TestExpandAbsolutePathWithRelativeParts(string input, string expected)
{
var result = PathUtilities.ExpandAbsolutePathWithRelativeParts(input);
Assert.Equal(expected, result);
}
}
}
|