File: FileUtilities_Tests.cs
Web Access
Project: ..\..\..\src\Framework.UnitTests\Microsoft.Build.Framework.UnitTests.csproj (Microsoft.Build.Framework.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.UnitTests;
using Shouldly;
using Xunit;
 
#nullable disable
 
namespace Microsoft.Build.Framework.UnitTests;
 
public class FileUtilities_Tests
{
    /// <summary>
    /// Exercises ItemSpecModifiers.GetItemSpecModifier
    /// </summary>
    [Fact]
    [Trait("Category", "netcore-osx-failing")]
    [Trait("Category", "netcore-linux-failing")]
    public void GetItemSpecModifier()
    {
        TestGetItemSpecModifier(Directory.GetCurrentDirectory());
        TestGetItemSpecModifier(null);
    }
 
    private static void TestGetItemSpecModifier(string currentDirectory)
    {
        string cache = null;
        string modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, "foo", String.Empty, ItemSpecModifiers.RecursiveDir, ref cache);
        Assert.Equal(String.Empty, modifier);
 
        cache = null;
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, "foo", String.Empty, ItemSpecModifiers.ModifiedTime, ref cache);
        Assert.Equal(String.Empty, modifier);
 
        cache = null;
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"foo\goo", String.Empty, ItemSpecModifiers.RelativeDir, ref cache);
        Assert.Equal(@"foo" + Path.DirectorySeparatorChar, modifier);
 
        // confirm we get the same thing back the second time
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"foo\goo", String.Empty, ItemSpecModifiers.RelativeDir, ref cache);
        Assert.Equal(@"foo" + Path.DirectorySeparatorChar, modifier);
 
        cache = null;
        string itemSpec = NativeMethodsShared.IsWindows ? @"c:\foo.txt" : "/foo.txt";
        string itemSpecDir = NativeMethodsShared.IsWindows ? @"c:\" : "/";
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.FullPath, ref cache);
        Assert.Equal(itemSpec, modifier);
        Assert.Equal(itemSpec, cache);
 
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.RootDir, ref cache);
        Assert.Equal(itemSpecDir, modifier);
 
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.Filename, ref cache);
        Assert.Equal(@"foo", modifier);
 
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.Extension, ref cache);
        Assert.Equal(@".txt", modifier);
 
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.Directory, ref cache);
        Assert.Equal(String.Empty, modifier);
 
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.Identity, ref cache);
        Assert.Equal(itemSpec, modifier);
 
        string projectPath = NativeMethodsShared.IsWindows ? @"c:\abc\goo.proj" : @"/abc/goo.proj";
        string projectPathDir = NativeMethodsShared.IsWindows ? @"c:\abc\" : @"/abc/";
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, ItemSpecModifiers.DefiningProjectDirectory, ref cache);
        Assert.Equal(projectPathDir, modifier);
 
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, ItemSpecModifiers.DefiningProjectExtension, ref cache);
        Assert.Equal(@".proj", modifier);
 
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, ItemSpecModifiers.DefiningProjectFullPath, ref cache);
        Assert.Equal(projectPath, modifier);
 
        modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, ItemSpecModifiers.DefiningProjectName, ref cache);
        Assert.Equal(@"goo", modifier);
    }
 
    [Fact]
    public void MakeRelativeTests()
    {
        if (NativeMethodsShared.IsWindows)
        {
            Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"c:\abc\def\foo.cpp"));
            Assert.Equal(@"def\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def\foo.cpp"));
            Assert.Equal(@"..\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\foo.cpp"));
            Assert.Equal(@"..\ttt\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ttt\foo.cpp"));
            Assert.Equal(@"e:\abc\def\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"e:\abc\def\foo.cpp"));
            Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"\\aaa\abc\def", @"\\aaa\abc\def\foo.cpp"));
            Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"foo.cpp"));
            Assert.Equal(@"\\host\path\file", FileUtilities.MakeRelative(@"c:\abc\def", @"\\host\path\file"));
            Assert.Equal(@"\\host\d$\file", FileUtilities.MakeRelative(@"c:\abc\def", @"\\host\d$\file"));
            Assert.Equal(@"..\fff\ggg.hh", FileUtilities.MakeRelative(@"c:\foo\bar\..\abc\cde", @"c:\foo\bar\..\abc\fff\ggg.hh"));
 
            /* Directories */
            Assert.Equal(@"def\", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def\"));
            Assert.Equal(@"..\", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\"));
            Assert.Equal(@"..\ttt\", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ttt\"));
            Assert.Equal(@".", FileUtilities.MakeRelative(@"c:\abc\def\", @"c:\abc\def\"));
 
            /* Directory + File */
            Assert.Equal(@"def", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def"));
            Assert.Equal(@"..\..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\ghi"));
            Assert.Equal(@"..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ghi"));
            Assert.Equal(@"..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\", @"c:\abc\ghi"));
 
            /* File + Directory */
            Assert.Equal(@"def\", FileUtilities.MakeRelative(@"c:\abc", @"c:\abc\def\"));
            Assert.Equal(@"..\", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\"));
            Assert.Equal(@"..\ghi\", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\ghi\"));
            Assert.Equal(@".", FileUtilities.MakeRelative(@"c:\abc\def", @"c:\abc\def\"));
        }
        else
        {
            Assert.Equal(@"bar.cpp", FileUtilities.MakeRelative(@"/abc/def", @"/abc/def/bar.cpp"));
            Assert.Equal(@"def/foo.cpp", FileUtilities.MakeRelative(@"/abc/", @"/abc/def/foo.cpp"));
            Assert.Equal(@"../foo.cpp", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/foo.cpp"));
            Assert.Equal(@"../ttt/foo.cpp", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ttt/foo.cpp"));
            Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"/abc/def", @"foo.cpp"));
            Assert.Equal(@"../fff/ggg.hh", FileUtilities.MakeRelative(@"/foo/bar/../abc/cde", @"/foo/bar/../abc/fff/ggg.hh"));
 
            /* Directories */
            Assert.Equal(@"def/", FileUtilities.MakeRelative(@"/abc/", @"/abc/def/"));
            Assert.Equal(@"../", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/"));
            Assert.Equal(@"../ttt/", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ttt/"));
            Assert.Equal(@".", FileUtilities.MakeRelative(@"/abc/def/", @"/abc/def/"));
 
            /* Directory + File */
            Assert.Equal(@"def", FileUtilities.MakeRelative(@"/abc/", @"/abc/def"));
            Assert.Equal(@"../../ghi", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/ghi"));
            Assert.Equal(@"../ghi", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ghi"));
            Assert.Equal(@"../ghi", FileUtilities.MakeRelative(@"/abc/def/", @"/abc/ghi"));
 
            /* File + Directory */
            Assert.Equal(@"def/", FileUtilities.MakeRelative(@"/abc", @"/abc/def/"));
            Assert.Equal(@"../", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/"));
            Assert.Equal(@"../ghi/", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/ghi/"));
            Assert.Equal(@".", FileUtilities.MakeRelative(@"/abc/def", @"/abc/def/"));
        }
    }
 
    /// <summary>
    /// Exercises ItemSpecModifiers.GetItemSpecModifier on a bad path.
    /// </summary>
    [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")]
    public void GetItemSpecModifierOnBadPath()
    {
        Assert.Throws<InvalidOperationException>(() =>
        {
            TestGetItemSpecModifierOnBadPath(Directory.GetCurrentDirectory());
        });
    }
    /// <summary>
    /// Exercises ItemSpecModifiers.GetItemSpecModifier on a bad path.
    /// </summary>
    [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")]
    public void GetItemSpecModifierOnBadPath2()
    {
        Assert.Throws<InvalidOperationException>(() =>
        {
            TestGetItemSpecModifierOnBadPath(null);
        });
    }
 
    private static void TestGetItemSpecModifierOnBadPath(string currentDirectory)
    {
        try
        {
            string cache = null;
            ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"http://www.microsoft.com", String.Empty, ItemSpecModifiers.RootDir, ref cache);
        }
        catch (Exception e)
        {
            // so I can see the exception message in NUnit's "Standard Out" window
            Console.WriteLine(e.Message);
            throw;
        }
    }
 
    [Fact]
    public void GetFileInfoNoThrowBasic()
    {
        string file = null;
        try
        {
            file = FileUtilities.GetTemporaryFile();
            FileInfo info = FileUtilities.GetFileInfoNoThrow(file);
            Assert.Equal(info.LastWriteTime, new FileInfo(file).LastWriteTime);
        }
        finally
        {
            if (file != null)
            {
                File.Delete(file);
            }
        }
    }
 
    [Fact]
    public void GetFileInfoNoThrowNonexistent()
    {
        FileInfo info = FileUtilities.GetFileInfoNoThrow("this_file_is_nonexistent");
        Assert.Null(info);
    }
 
    /// <summary>
    /// Exercises FrameworkFileUtilities.EndsWithSlash
    /// </summary>
    [Fact]
    [Trait("Category", "netcore-osx-failing")]
    [Trait("Category", "netcore-linux-failing")]
    public void EndsWithSlash()
    {
        Assert.True(FileUtilities.EndsWithSlash(@"C:\foo\"));
        Assert.True(FileUtilities.EndsWithSlash(@"C:\"));
        Assert.True(FileUtilities.EndsWithSlash(@"\"));
 
        Assert.True(FileUtilities.EndsWithSlash(@"http://www.microsoft.com/"));
        Assert.True(FileUtilities.EndsWithSlash(@"//server/share/"));
        Assert.True(FileUtilities.EndsWithSlash(@"/"));
 
        Assert.False(FileUtilities.EndsWithSlash(@"C:\foo"));
        Assert.False(FileUtilities.EndsWithSlash(@"C:"));
        Assert.False(FileUtilities.EndsWithSlash(@"foo"));
 
        // confirm that empty string doesn't barf
        Assert.False(FileUtilities.EndsWithSlash(String.Empty));
    }
 
    /// <summary>
    /// Exercises FileUtilities.GetDirectory
    /// </summary>
    [Fact]
    [Trait("Category", "netcore-osx-failing")]
    [Trait("Category", "netcore-linux-failing")]
    public void GetDirectoryWithTrailingSlash()
    {
        Assert.Equal(NativeMethodsShared.IsWindows ? @"c:\" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:\" : "/"));
        Assert.Equal(NativeMethodsShared.IsWindows ? @"c:\" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:\foo" : "/foo"));
        Assert.Equal(NativeMethodsShared.IsWindows ? @"c:" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:" : "/"));
        Assert.Equal(FileUtilities.FixFilePath(@"\"), FileUtilities.GetDirectory(@"\"));
        Assert.Equal(FileUtilities.FixFilePath(@"\"), FileUtilities.GetDirectory(@"\foo"));
        Assert.Equal(FileUtilities.FixFilePath(@"..\"), FileUtilities.GetDirectory(@"..\foo"));
        Assert.Equal(FileUtilities.FixFilePath(@"\foo\"), FileUtilities.GetDirectory(@"\foo\"));
        Assert.Equal(FileUtilities.FixFilePath(@"\\server\share"), FileUtilities.GetDirectory(@"\\server\share"));
        Assert.Equal(FileUtilities.FixFilePath(@"\\server\share\"), FileUtilities.GetDirectory(@"\\server\share\"));
        Assert.Equal(FileUtilities.FixFilePath(@"\\server\share\"), FileUtilities.GetDirectory(@"\\server\share\file"));
        Assert.Equal(FileUtilities.FixFilePath(@"\\server\share\directory\"), FileUtilities.GetDirectory(@"\\server\share\directory\"));
        Assert.Equal(FileUtilities.FixFilePath(@"foo\"), FileUtilities.GetDirectory(@"foo\bar"));
        Assert.Equal(FileUtilities.FixFilePath(@"\foo\bar\"), FileUtilities.GetDirectory(@"\foo\bar\"));
        Assert.Equal(String.Empty, FileUtilities.GetDirectory("foo"));
    }
 
    [Theory]
    [InlineData("foo.txt", new[] { ".txt" })]
    [InlineData("foo.txt", new[] { ".TXT" })]
    [InlineData("foo.txt", new[] { ".EXE", ".TXT" })]
    public void HasExtension_WhenFileNameHasExtension_ReturnsTrue(string fileName, string[] allowedExtensions)
    {
        var result = FileUtilities.HasExtension(fileName, allowedExtensions);
 
        if (!FileUtilities.IsFileSystemCaseSensitive || allowedExtensions.Any(extension => fileName.Contains(extension)))
        {
            result.ShouldBeTrue();
        }
    }
 
    [Theory]
    [InlineData("foo.txt", new[] { ".DLL" })]
    [InlineData("foo.txt", new[] { ".EXE", ".DLL" })]
    [InlineData("foo.exec", new[] { ".exe", })]
    [InlineData("foo.exe", new[] { ".exec", })]
    [InlineData("foo", new[] { ".exe", })]
    [InlineData("", new[] { ".exe" })]
    [InlineData(null, new[] { ".exe" })]
    public void HasExtension_WhenFileNameDoesNotHaveExtension_ReturnsFalse(string fileName, string[] allowedExtensions)
    {
        var result = FileUtilities.HasExtension(fileName, allowedExtensions);
 
        Assert.False(result);
    }
 
    [WindowsFullFrameworkOnlyFact]
    public void HasExtension_WhenInvalidFileName_ThrowsArgumentException()
    {
        Assert.Throws<ArgumentException>(() =>
        {
            FileUtilities.HasExtension("|/", new[] { ".exe" });
        });
    }
 
    [Fact]
    public void HasExtension_UsesOrdinalIgnoreCase()
    {
        var currentCulture = Thread.CurrentThread.CurrentCulture;
        try
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR"); // Turkish
 
            var result = FileUtilities.HasExtension("foo.ini", new string[] { ".INI" });
 
            result.ShouldBe(!FileUtilities.IsFileSystemCaseSensitive);
        }
        finally
        {
            Thread.CurrentThread.CurrentCulture = currentCulture;
        }
    }
 
    /// <summary>
    /// Exercises FrameworkFileUtilities.EnsureTrailingSlash
    /// </summary>
    [Fact]
    public void EnsureTrailingSlash()
    {
        // Doesn't have a trailing slash to start with.
        Assert.Equal(FileUtilities.FixFilePath(@"foo\bar\"), FileUtilities.EnsureTrailingSlash(@"foo\bar")); // "test 1"
        Assert.Equal(FileUtilities.FixFilePath(@"foo/bar\"), FileUtilities.EnsureTrailingSlash(@"foo/bar")); // "test 2"
 
        // Already has a trailing slash to start with.
        Assert.Equal(FileUtilities.FixFilePath(@"foo/bar/"), FileUtilities.EnsureTrailingSlash(@"foo/bar/")); // test 3"
        Assert.Equal(FileUtilities.FixFilePath(@"foo\bar\"), FileUtilities.EnsureTrailingSlash(@"foo\bar\")); // test 4"
        Assert.Equal(FileUtilities.FixFilePath(@"foo/bar\"), FileUtilities.EnsureTrailingSlash(@"foo/bar\")); // test 5"
        Assert.Equal(FileUtilities.FixFilePath(@"foo\bar/"), FileUtilities.EnsureTrailingSlash(@"foo\bar/")); // "test 5"
    }
 
    /// <summary>
    /// Exercises ItemSpecModifiers.IsItemSpecModifier
    /// </summary>
    [Fact]
    public void IsItemSpecModifier()
    {
        // Positive matches using exact case.
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("FullPath")); // "test 1"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("RootDir")); // "test 2"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("Filename")); // "test 3"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("Extension")); // "test 4"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("RelativeDir")); // "test 5"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("Directory")); // "test 6"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("RecursiveDir")); // "test 7"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("Identity")); // "test 8"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("ModifiedTime")); // "test 9"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("CreatedTime")); // "test 10"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("AccessedTime")); // "test 11"
 
        // Positive matches using different case.
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("fullPath")); // "test 21"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("rootDir")); // "test 22"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("filename")); // "test 23"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("extension")); // "test 24"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("relativeDir")); // "test 25"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("directory")); // "test 26"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("recursiveDir")); // "test 27"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("identity")); // "test 28"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("modifiedTime")); // "test 29"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("createdTime")); // "test 30"
        Assert.True(ItemSpecModifiers.IsItemSpecModifier("accessedTime")); // "test 31"
 
        // Negative tests to get maximum code coverage inside the many different branches
        // of ItemSpecModifiers.IsItemSpecModifier.
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("rootxxx")); // "test 41"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Rootxxx")); // "test 42"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxx")); // "test 43"
 
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("filexxxx")); // "test 44"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Filexxxx")); // "test 45"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("idenxxxx")); // "test 46"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Idenxxxx")); // "test 47"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxx")); // "test 48"
 
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("extenxxxx")); // "test 49"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Extenxxxx")); // "test 50"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("direcxxxx")); // "test 51"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Direcxxxx")); // "test 52"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxx")); // "test 53"
 
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxx")); // "test 54"
 
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("relativexxx")); // "test 55"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Relativexxx")); // "test 56"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("createdxxxx")); // "test 57"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Createdxxxx")); // "test 58"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxxx")); // "test 59"
 
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("recursivexxx")); // "test 60"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Recursivexxx")); // "test 61"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("accessedxxxx")); // "test 62"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Accessedxxxx")); // "test 63"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("modifiedxxxx")); // "test 64"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("Modifiedxxxx")); // "test 65"
        Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxxxx")); // "test 66"
 
        Assert.False(ItemSpecModifiers.IsItemSpecModifier(null)); // "test 67"
    }
 
    [Fact]
    public void CheckDerivableItemSpecModifiers()
    {
        Assert.True(ItemSpecModifiers.IsDerivableItemSpecModifier("Filename"));
        Assert.False(ItemSpecModifiers.IsDerivableItemSpecModifier("RecursiveDir"));
        Assert.False(ItemSpecModifiers.IsDerivableItemSpecModifier("recursivedir"));
    }
 
    [WindowsOnlyFact]
    public void NormalizePathThatFitsIntoMaxPath()
    {
        string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890";
        string filePath = @"..\..\..\..\..\..\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs";
        string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs";
 
        Assert.Equal(fullPath, FileUtilities.NormalizePath(Path.Combine(currentDirectory, filePath)));
    }
 
    [LongPathSupportDisabledFact(fullFrameworkOnly: true, additionalMessage: "https://github.com/dotnet/msbuild/issues/4363")]
    public void NormalizePathThatDoesntFitIntoMaxPath()
    {
        Assert.Throws<PathTooLongException>(() =>
        {
            string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890";
            string filePath = @"..\..\..\..\..\..\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs";
 
            // This path ends up over 420 characters long
            string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs";
 
            Assert.Equal(fullPath, FileUtilities.NormalizePath(Path.Combine(currentDirectory, filePath)));
        });
    }
 
    [WindowsOnlyFact]
    public void GetItemSpecModifierRootDirThatFitsIntoMaxPath()
    {
        string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890";
        string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs";
        string cache = fullPath;
 
        Assert.Equal(@"c:\", ItemSpecModifiers.GetItemSpecModifier(currentDirectory, fullPath, String.Empty, ItemSpecModifiers.RootDir, ref cache));
    }
 
    [Fact]
    public void NormalizePathNull()
    {
        Assert.Throws<ArgumentNullException>(() =>
        {
            Assert.Null(FileUtilities.NormalizePath(null, null));
        });
    }
 
    [Fact]
    public void NormalizePathEmpty()
    {
        Assert.Throws<ArgumentException>(() =>
        {
            Assert.Null(FileUtilities.NormalizePath(String.Empty));
        });
    }
 
    [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")]
    public void NormalizePathBadUNC1()
    {
        Assert.Throws<ArgumentException>(() =>
        {
            Assert.Null(FileUtilities.NormalizePath(@"\\"));
        });
    }
 
    [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")]
    public void NormalizePathBadUNC2()
    {
        Assert.Throws<ArgumentException>(() =>
        {
            Assert.Null(FileUtilities.NormalizePath(@"\\XXX\"));
        });
    }
 
    [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")]
    public void NormalizePathBadUNC3()
    {
        Assert.Throws<ArgumentException>(() =>
        {
            Assert.Equal(@"\\localhost", FileUtilities.NormalizePath(@"\\localhost"));
        });
    }
 
    [WindowsOnlyFact]
    public void NormalizePathGoodUNC()
    {
        Assert.Equal(@"\\localhost\share", FileUtilities.NormalizePath(@"\\localhost\share"));
    }
 
    [WindowsOnlyFact]
    public void NormalizePathTooLongWithDots()
    {
        string longPart = new string('x', 300);
        Assert.Equal(@"c:\abc\def", FileUtilities.NormalizePath(@"c:\abc\" + longPart + @"\..\def"));
    }
 
    [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")]
    public void NormalizePathInvalid()
    {
        string filePath = @"c:\aardvark\|||";
 
        Assert.Throws<ArgumentException>(() =>
        {
            FileUtilities.NormalizePath(filePath);
        });
    }
 
    [WindowsOnlyFact]
    public void CannotNormalizePathWithNewLineAndSpace()
    {
        string filePath = "\r\n      C:\\work\\sdk3\\artifacts\\tmp\\Debug\\SimpleNamesWi---6143883E\\NETFrameworkLibrary\\bin\\Debug\\net462\\NETFrameworkLibrary.dll\r\n      ";
 
#if FEATURE_LEGACY_GETFULLPATH
        Assert.Throws<ArgumentException>(() => FileUtilities.NormalizePath(filePath));
#else
        Assert.NotEqual("C:\\work\\sdk3\\artifacts\\tmp\\Debug\\SimpleNamesWi---6143883E\\NETFrameworkLibrary\\bin\\Debug\\net462\\NETFrameworkLibrary.dll", FileUtilities.NormalizePath(filePath));
#endif
    }
 
    [Fact]
    public void FileOrDirectoryExistsNoThrow()
    {
        var isWindows = NativeMethodsShared.IsWindows;
 
        Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow("||"));
        Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(isWindows ? @"c:\doesnot_exist" : "/doesnot_exist"));
        Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(isWindows ? @"c:\" : "/"));
        Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(Path.GetTempPath()));
 
        string path = null;
 
        try
        {
            path = FileUtilities.GetTemporaryFile();
            Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(path));
        }
        finally
        {
            File.Delete(path);
        }
    }
 
#if FEATURE_ENVIRONMENT_SYSTEMDIRECTORY
    // These tests will need to be redesigned for Linux
 
    [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))]
    public void FileOrDirectoryExistsNoThrowTooLongWithDots()
    {
        int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length;
 
        string longPart = new string('x', 260 - length); // We want the shortest that is > max path.
 
        string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3);
        Console.WriteLine(inputPath.Length);
 
        // "c:\windows\system32\<verylong>\..\..\windows\system32" exists
        Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath));
        Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath.Replace('\\', 'X')));
    }
 
    [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))]
    public void FileOrDirectoryExistsNoThrowTooLongWithDotsRelative()
    {
        int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length;
 
        string longPart = new string('x', 260 - length); // We want the shortest that is > max path.
 
        string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3);
        Console.WriteLine(inputPath.Length);
 
        // "c:\windows\system32\<verylong>\..\..\windows\system32" exists
 
        string currentDirectory = Directory.GetCurrentDirectory();
 
        try
        {
            Directory.SetCurrentDirectory(Environment.SystemDirectory);
 
            Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath));
            Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath.Replace('\\', 'X')));
        }
        finally
        {
            Directory.SetCurrentDirectory(currentDirectory);
        }
    }
 
    [Fact]
    public void DirectoryExistsNoThrowTooLongWithDots()
    {
        string path = Path.Combine(Environment.SystemDirectory, "..", "..", "..") + Path.DirectorySeparatorChar;
        if (NativeMethodsShared.IsWindows)
        {
            path += Environment.SystemDirectory.Substring(3);
        }
 
        int length = path.Length;
 
        string longPart = new string('x', 260 - length); // We want the shortest that is > max path.
 
        string inputPath = Path.Combine(new[] { Environment.SystemDirectory, longPart, "..", "..", ".." })
                           + Path.DirectorySeparatorChar;
        if (NativeMethodsShared.IsWindows)
        {
            path += Environment.SystemDirectory.Substring(3);
        }
 
        Console.WriteLine(inputPath.Length);
 
        // "c:\windows\system32\<verylong>\..\..\windows\system32" exists
        Assert.True(FileUtilities.DirectoryExistsNoThrow(inputPath));
    }
 
    [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))]
    public void DirectoryExistsNoThrowTooLongWithDotsRelative()
    {
        int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length;
 
        string longPart = new string('x', 260 - length); // We want the shortest that is > max path.
 
        string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3);
        Console.WriteLine(inputPath.Length);
 
        // "c:\windows\system32\<verylong>\..\..\..\windows\system32" exists
 
        string currentDirectory = Directory.GetCurrentDirectory();
 
        try
        {
            Directory.SetCurrentDirectory(Environment.SystemDirectory);
 
            FileUtilities.DirectoryExistsNoThrow(inputPath).ShouldBeTrue();
            FileUtilities.DirectoryExistsNoThrow(inputPath.Replace('\\', 'X')).ShouldBeFalse();
        }
        finally
        {
            Directory.SetCurrentDirectory(currentDirectory);
        }
    }
 
    public static bool RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241()
    {
        // Run these tests only when we're not on Windows
        return !NativeMethodsShared.IsWindows ||
        // OR we're on Windows and long paths aren't enabled
        // https://github.com/dotnet/msbuild/issues/4241
               NativeMethodsShared.IsMaxPathLegacyWindows();
    }
 
    [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))]
    public void FileExistsNoThrowTooLongWithDots()
    {
        int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length;
 
        string longPart = new string('x', 260 - length); // We want the shortest that is > max path.
 
        string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe";
        Console.WriteLine(inputPath.Length);
        Console.WriteLine(inputPath);
 
        // "c:\windows\system32\<verylong>\..\..\windows\system32" exists
        Assert.True(FileUtilities.FileExistsNoThrow(inputPath));
    }
 
    [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))]
    public void FileExistsNoThrowTooLongWithDotsRelative()
    {
        int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length;
 
        string longPart = new string('x', 260 - length); // We want the shortest that is > max path.
 
        string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe";
        Console.WriteLine(inputPath.Length);
 
        // "c:\windows\system32\<verylong>\..\..\windows\system32" exists
 
        string currentDirectory = Directory.GetCurrentDirectory();
 
        try
        {
            Directory.SetCurrentDirectory(Environment.SystemDirectory);
 
            Assert.True(FileUtilities.FileExistsNoThrow(inputPath));
            Assert.False(FileUtilities.FileExistsNoThrow(inputPath.Replace('\\', 'X')));
        }
        finally
        {
            Directory.SetCurrentDirectory(currentDirectory);
        }
    }
 
    [Fact]
    public void GetFileInfoNoThrowTooLongWithDots()
    {
        int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length;
 
        string longPart = new string('x', 260 - length); // We want the shortest that is > max path.
 
        string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe";
        Console.WriteLine(inputPath.Length);
 
        // "c:\windows\system32\<verylong>\..\..\windows\system32" exists
        Assert.True(FileUtilities.GetFileInfoNoThrow(inputPath) != null);
        Assert.False(FileUtilities.GetFileInfoNoThrow(inputPath.Replace('\\', 'X')) != null);
    }
 
    [Fact]
    public void GetFileInfoNoThrowTooLongWithDotsRelative()
    {
        int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length;
 
        string longPart = new string('x', 260 - length); // We want the shortest that is > max path.
 
        string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe";
        Console.WriteLine(inputPath.Length);
 
        // "c:\windows\system32\<verylong>\..\..\windows\system32" exists
 
        string currentDirectory = Directory.GetCurrentDirectory();
 
        try
        {
            Directory.SetCurrentDirectory(Environment.SystemDirectory);
 
            Assert.True(FileUtilities.GetFileInfoNoThrow(inputPath) != null);
            Assert.False(FileUtilities.GetFileInfoNoThrow(inputPath.Replace('\\', 'X')) != null);
        }
        finally
        {
            Directory.SetCurrentDirectory(currentDirectory);
        }
    }
#endif
 
    /// <summary>
    /// Simple test, neither the base file nor retry files exist
    /// </summary>
    [Fact]
    public void GenerateTempFileNameSimple()
    {
        string path = null;
 
        try
        {
            path = FileUtilities.GetTemporaryFile();
 
            Assert.EndsWith(".tmp", path);
            Assert.True(File.Exists(path));
            Assert.StartsWith(Path.GetTempPath(), path);
        }
        finally
        {
            File.Delete(path);
        }
    }
 
    /// <summary>
    /// Choose an extension
    /// </summary>
    [Fact]
    public void GenerateTempFileNameWithExtension()
    {
        string path = null;
 
        try
        {
            path = FileUtilities.GetTemporaryFile(".bat");
 
            Assert.EndsWith(".bat", path);
            Assert.True(File.Exists(path));
            Assert.StartsWith(Path.GetTempPath(), path);
        }
        finally
        {
            File.Delete(path);
        }
    }
 
    /// <summary>
    /// Choose a (missing) directory and extension
    /// </summary>
    [Fact]
    public void GenerateTempFileNameWithDirectoryAndExtension()
    {
        string path = null;
        string directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "subfolder");
 
        try
        {
            path = FileUtilities.GetTemporaryFile(directory, null, ".bat");
 
            Assert.EndsWith(".bat", path);
            Assert.True(File.Exists(path));
            Assert.StartsWith(directory, path);
        }
        finally
        {
            File.Delete(path);
            FileUtilities.DeleteWithoutTrailingBackslash(directory);
        }
    }
 
    /// <summary>
    /// Extension without a period
    /// </summary>
    [Fact]
    public void GenerateTempFileNameWithExtensionNoPeriod()
    {
        string path = null;
 
        try
        {
            path = FileUtilities.GetTemporaryFile("bat");
 
            Assert.EndsWith(".bat", path);
            Assert.True(File.Exists(path));
            Assert.StartsWith(Path.GetTempPath(), path);
        }
        finally
        {
            File.Delete(path);
        }
    }
 
    /// <summary>
    /// Extension is invalid
    /// </summary>
    [Fact]
    [Trait("Category", "netcore-osx-failing")]
    [Trait("Category", "netcore-linux-failing")]
    public void GenerateTempBatchFileWithBadExtension()
    {
        Assert.Throws<IOException>(() =>
        {
            FileUtilities.GetTemporaryFile("|");
        });
    }
 
    /// <summary>
    /// Directory is invalid
    /// </summary>
    [Fact]
    [Trait("Category", "netcore-osx-failing")]
    [Trait("Category", "netcore-linux-failing")]
    public void GenerateTempBatchFileWithBadDirectory()
    {
        Assert.Throws<IOException>(() =>
        {
            FileUtilities.GetTemporaryFile("|", null, ".tmp");
        });
    }
 
    [UnixOnlyFact]
    public void AbsolutePathLooksLikeUnixPathOnUnix()
    {
        var secondSlash = SystemSpecificAbsolutePath.Substring(1).IndexOf(Path.DirectorySeparatorChar) + 1;
        var rootLevelPath = SystemSpecificAbsolutePath.Substring(0, secondSlash);
 
        Assert.True(FileUtilities.LooksLikeUnixFilePath(SystemSpecificAbsolutePath));
        Assert.True(FileUtilities.LooksLikeUnixFilePath(rootLevelPath));
    }
 
    [WindowsOnlyFact]
    public void PathDoesNotLookLikeUnixPathOnWindows()
    {
        Assert.False(FileUtilities.LooksLikeUnixFilePath(SystemSpecificAbsolutePath));
        Assert.False(FileUtilities.LooksLikeUnixFilePath("/path/that/looks/unixy"));
        Assert.False(FileUtilities.LooksLikeUnixFilePath("/root"));
    }
 
    [UnixOnlyFact]
    public void RelativePathLooksLikeUnixPathOnUnixWithBaseDirectory()
    {
        string filePath = ObjectModelHelpers.CreateFileInTempProjectDirectory("first/second/file.txt", String.Empty);
        string oldCWD = Directory.GetCurrentDirectory();
 
        try
        {
            // <tmp_dir>/first
            string firstDirectory = Path.GetDirectoryName(Path.GetDirectoryName(filePath));
            string tmpDirectory = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(filePath)));
 
            Directory.SetCurrentDirectory(tmpDirectory);
 
            // We are in <tmp_dir> and second is not under that, so this will be false
            Assert.False(FileUtilities.LooksLikeUnixFilePath("second/file.txt"));
 
            // .. but if we have baseDirectory:firstDirectory, then it will be true
            Assert.True(FileUtilities.LooksLikeUnixFilePath("second/file.txt", firstDirectory));
        }
        finally
        {
            if (filePath != null)
            {
                File.Delete(filePath);
            }
            Directory.SetCurrentDirectory(oldCWD);
        }
    }
 
    [UnixOnlyFact]
    public void RelativePathMaybeAdjustFilePathWithBaseDirectory()
    {
        // <tmp_dir>/first/second/file.txt
        string filePath = ObjectModelHelpers.CreateFileInTempProjectDirectory("first/second/file.txt", String.Empty);
        string oldCWD = Directory.GetCurrentDirectory();
 
        try
        {
            // <tmp_dir>/first
            string firstDirectory = Path.GetDirectoryName(Path.GetDirectoryName(filePath));
            string tmpDirectory = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(filePath)));
 
            Directory.SetCurrentDirectory(tmpDirectory);
 
            // We are in <tmp_dir> and second is not under that, so this won't convert
            Assert.Equal("second\\file.txt", FileUtilities.MaybeAdjustFilePath("second\\file.txt"));
 
            // .. but if we have baseDirectory:firstDirectory, then it will
            Assert.Equal("second/file.txt", FileUtilities.MaybeAdjustFilePath("second\\file.txt", firstDirectory));
        }
        finally
        {
            if (filePath != null)
            {
                File.Delete(filePath);
            }
            Directory.SetCurrentDirectory(oldCWD);
        }
    }
 
    private static string SystemSpecificAbsolutePath => BuildEnvironmentHelper.ExecutingAssemblyPath;
 
 
    [Fact]
    public void GetFolderAboveTest()
    {
        string root = NativeMethodsShared.IsWindows ? @"c:\" : "/";
        string path = Path.Combine(root, "1", "2", "3", "4", "5");
 
        Assert.Equal(Path.Combine(root, "1", "2", "3", "4", "5"), FileUtilities.GetFolderAbove(path, 0));
        Assert.Equal(Path.Combine(root, "1", "2", "3", "4"), FileUtilities.GetFolderAbove(path));
        Assert.Equal(Path.Combine(root, "1", "2", "3"), FileUtilities.GetFolderAbove(path, 2));
        Assert.Equal(Path.Combine(root, "1", "2"), FileUtilities.GetFolderAbove(path, 3));
        Assert.Equal(Path.Combine(root, "1"), FileUtilities.GetFolderAbove(path, 4));
        Assert.Equal(root, FileUtilities.GetFolderAbove(path, 5));
        Assert.Equal(root, FileUtilities.GetFolderAbove(path, 99));
 
        Assert.Equal(root, FileUtilities.GetFolderAbove(root, 99));
    }
 
    [Fact]
    public void CombinePathsTest()
    {
        // These tests run in .NET 4+, so we can cheat
        var root = @"c:\";
 
        Assert.Equal(
            Path.Combine(root, "path1"),
            FileUtilities.CombinePaths(root, "path1"));
 
        Assert.Equal(
            Path.Combine(root, "path1", "path2", "file.txt"),
            FileUtilities.CombinePaths(root, "path1", "path2", "file.txt"));
    }
 
    [Theory]
    [InlineData(@"c:\a\.\b", true)]
    [InlineData(@"c:\a\..\b", true)]
    [InlineData(@"c:\a\..", true)]
    [InlineData(@"c:\a\.", true)]
    [InlineData(@".\a", true)]
    [InlineData(@"..\b", true)]
    [InlineData(@"..", true)]
    [InlineData(@".", true)]
    [InlineData(@"..\", true)]
    [InlineData(@".\", true)]
    [InlineData(@"\..", true)]
    [InlineData(@"\.", true)]
    [InlineData(@"..\..\a", true)]
    [InlineData(@"..\..\..\a", true)]
    [InlineData(@"b..\", false)]
    [InlineData(@"b.\", false)]
    [InlineData(@"\b..", false)]
    [InlineData(@"\b.", false)]
    [InlineData(@"\b..\", false)]
    [InlineData(@"\b.\", false)]
    [InlineData(@"...", false)]
    [InlineData(@"....", false)]
    public void ContainsRelativeSegmentsTest(string path, bool expectedResult)
    {
        FileUtilities.ContainsRelativePathSegments(path).ShouldBe(expectedResult);
    }
 
    [Theory]
    [InlineData("a/b/c/d", 0, "")]
    [InlineData("a/b/c/d", 1, "d")]
    [InlineData("a/b/c/d", 2, "c/d")]
    [InlineData("a/b/c/d", 3, "b/c/d")]
    [InlineData("a/b/c/d", 4, "a/b/c/d")]
    [InlineData("a/b/c/d", 5, "a/b/c/d")]
    [InlineData(@"a\/\/\//b/\/\/\//c//\/\/\/d/\//\/\/", 2, "c/d")]
    public static void TestTruncatePathToTrailingSegments(string path, int trailingSegments, string expectedTruncatedPath)
    {
        expectedTruncatedPath = expectedTruncatedPath.Replace('/', Path.DirectorySeparatorChar);
 
        FileUtilities.TruncatePathToTrailingSegments(path, trailingSegments).ShouldBe(expectedTruncatedPath);
    }
 
    /// <summary>
    /// Exercises FileUtilities.EnsureSingleQuotes
    /// </summary>
    [Theory]
    [InlineData(null, null)] // Null test
    [InlineData("", "")] // Empty string test
    [InlineData(@" ", @"' '")] // One character which is a space
    [InlineData(@"'", @"'''")] // One character which is a single quote
    [InlineData(@"""", @"'""'")] // One character which is a double quote
    [InlineData(@"example", @"'example'")] // Unquoted string
    [InlineData(@"'example'", @"'example'")] // Single quoted string
    [InlineData(@"""example""", @"'example'")] // Double quoted string
    [InlineData(@"'example""", @"''example""'")] // Mixed Quotes - Leading Single
    [InlineData(@"""example'", @"'""example''")] // Mixed Quotes - Leading Double
    [InlineData(@"ex""am'ple", @"'ex""am'ple'")] // Interior Quotes
    public void EnsureSingleQuotesTest(string path, string expectedResult)
    {
        FileUtilities.EnsureSingleQuotes(path).ShouldBe(expectedResult);
    }
 
    /// <summary>
    /// Exercises FileUtilities.EnsureDoubleQuotes
    /// </summary>
    [Theory]
    [InlineData(null, null)] // Null test
    [InlineData("", "")] // Empty string test
    [InlineData(@" ", @""" """)] // One character which is a space
    [InlineData(@"'", @"""'""")] // One character which is a single quote
    [InlineData(@"""", @"""""""")] // One character which is a double quote
    [InlineData(@"example", @"""example""")] // Unquoted string
    [InlineData(@"'example'", @"""example""")] // Single quoted string
    [InlineData(@"""example""", @"""example""")] // Double quoted string
    [InlineData(@"'example""", @"""'example""""")] // Mixed Quotes - Leading Single
    [InlineData(@"""example'", @"""""example'""")] // Mixed Quotes - Leading Double
    [InlineData(@"ex""am'ple", @"""ex""am'ple""")] // Interior Quotes
    public void EnsureDoubleQuotesTest(string path, string expectedResult)
    {
        FileUtilities.EnsureDoubleQuotes(path).ShouldBe(expectedResult);
    }
}