File: RuntimeHostInfoTests.cs
Web Access
Project: src\src\Compilers\Core\MSBuildTaskTests\Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj (Microsoft.Build.Tasks.CodeAnalysis.UnitTests)
// 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.IO;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests;
 
public sealed class RuntimeHostInfoTests(ITestOutputHelper output)
{
    private readonly ITestOutputHelper _output = output;
 
    [Fact, WorkItem("https://github.com/dotnet/msbuild/issues/12669")]
    public void DotNetInPath()
    {
        var previousPath = Environment.GetEnvironmentVariable("PATH");
        try
        {
            using var tempRoot = new TempRoot();
            var testDir = tempRoot.CreateDirectory();
            var globalDotNetDir = testDir.CreateDirectory("global-dotnet");
            var globalDotNetExe = globalDotNetDir.CreateFile($"dotnet{PlatformInformation.ExeExtension}");
            Environment.SetEnvironmentVariable("PATH", globalDotNetDir.Path);
 
            Assert.Equal(globalDotNetDir.Path, RuntimeHostInfo.GetToolDotNetRoot(_output.WriteLine));
        }
        finally
        {
            Environment.SetEnvironmentVariable("PATH", previousPath);
        }
    }
 
    [Fact, WorkItem("https://github.com/dotnet/msbuild/issues/12669")]
    public void DotNetInPath_None()
    {
        var previousPath = Environment.GetEnvironmentVariable("PATH");
        try
        {
            Environment.SetEnvironmentVariable("PATH", "");
 
            Assert.Null(RuntimeHostInfo.GetToolDotNetRoot(_output.WriteLine));
        }
        finally
        {
            Environment.SetEnvironmentVariable("PATH", previousPath);
        }
    }
 
    [Fact, WorkItem("https://github.com/dotnet/msbuild/issues/12669")]
    public void DotNetInPath_Symlinked()
    {
        var previousPath = Environment.GetEnvironmentVariable("PATH");
        try
        {
            using var tempRoot = new TempRoot();
            var testDir = tempRoot.CreateDirectory();
            var globalDotNetDir = testDir.CreateDirectory("global-dotnet");
            var globalDotNetExe = globalDotNetDir.CreateFile($"dotnet{PlatformInformation.ExeExtension}");
            var binDir = testDir.CreateDirectory("bin");
            var symlinkPath = Path.Combine(binDir.Path, $"dotnet{PlatformInformation.ExeExtension}");
 
            // Create symlink from binDir to the actual dotnet executable
            File.CreateSymbolicLink(path: symlinkPath, pathToTarget: globalDotNetExe.Path);
 
            Environment.SetEnvironmentVariable("PATH", binDir.Path);
 
            Assert.Equal(globalDotNetDir.Path, RuntimeHostInfo.GetToolDotNetRoot(_output.WriteLine));
        }
        finally
        {
            Environment.SetEnvironmentVariable("PATH", previousPath);
        }
    }
}
 
#if !NET
file static class NativeMethods
{
    extension(File)
    {
        /// <remarks>
        /// Only used by tests currently (might need some hardening if this is to be used by production code).
        /// </remarks>
        public static void CreateSymbolicLink(string path, string pathToTarget)
        {
            bool ok = CreateSymbolicLink(
                lpSymlinkFileName: path,
                lpTargetFileName: pathToTarget,
                dwFlags: SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE);
            if (!ok)
            {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }
        }
    }
 
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CreateSymbolicLink(
        string lpSymlinkFileName,
        string lpTargetFileName,
        uint dwFlags);
 
    private const uint SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2;
}
#endif