File: CommandTests\Restore\GivenThatIWantToRestoreApp.cs
Web Access
Project: ..\..\..\test\dotnet.Tests\dotnet.Tests.csproj (dotnet.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.DotNet.Tools.Test.Utilities;
using Newtonsoft.Json.Linq;
using System.Runtime.InteropServices;
 
namespace Microsoft.DotNet.Restore.Test
{
    public class GivenThatIWantToRestoreApp : SdkTest
    {
        public GivenThatIWantToRestoreApp(ITestOutputHelper log) : base(log)
        {
        }
 
        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        public void ItRestoresAppToSpecificDirectory(bool useStaticGraphEvaluation)
        {
            var rootPath = _testAssetsManager.CreateTestDirectory(identifier: useStaticGraphEvaluation.ToString()).Path;
 
            string dir = "pkgs";
            string fullPath = Path.GetFullPath(Path.Combine(rootPath, dir));
 
            var sln = "TestAppWithSlnAndSolutionFolders";
            var projectDirectory = _testAssetsManager
                .CopyTestAsset(sln, identifier: useStaticGraphEvaluation.ToString())
                .WithSource()
                .Path;
 
            string[] args = new[] { "App.sln", "--packages", fullPath };
            args = HandleStaticGraphEvaluation(useStaticGraphEvaluation, args);
            new DotnetRestoreCommand(Log)
                 .WithWorkingDirectory(projectDirectory)
                 .Execute(args)
                 .Should()
                 .Pass()
                 .And.NotHaveStdErr();
 
            Directory.Exists(fullPath).Should().BeTrue();
            Directory.EnumerateFiles(fullPath, "*.dll", SearchOption.AllDirectories).Count().Should().BeGreaterThan(0);
        }
 
        [Theory]
        [InlineData(true, ".csproj")]
        [InlineData(false, ".csproj")]
        [InlineData(true, ".fsproj")]
        [InlineData(false, ".fsproj")]
        public void ItRestoresLibToSpecificDirectory(bool useStaticGraphEvaluation, string extension)
        {
            var testProject = new TestProject()
            {
                Name = "RestoreToDir",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
                TargetExtension = extension,
            };
 
            testProject.PackageReferences.Add(new TestPackageReference("Newtonsoft.Json", ToolsetInfo.GetNewtonsoftJsonPackageVersion()));
            if (extension == ".fsproj")
            {
                testProject.PackageReferences.Add(new TestPackageReference("FSharp.Core", "6.0.1", updatePackageReference: true));
            }
 
            var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: useStaticGraphEvaluation.ToString() + extension);
 
            var rootPath = Path.Combine(testAsset.TestRoot, testProject.Name);
 
            string dir = "pkgs";
            string fullPath = Path.GetFullPath(Path.Combine(rootPath, dir));
 
            string[] args = new[] { "--packages", dir };
            args = HandleStaticGraphEvaluation(useStaticGraphEvaluation, args);
            new DotnetRestoreCommand(Log)
                .WithWorkingDirectory(rootPath)
                .Execute(args)
                .Should()
                .Pass()
                .And.NotHaveStdErr();
 
            var dllCount = 0;
 
            if (Directory.Exists(fullPath))
            {
                dllCount = Directory.EnumerateFiles(fullPath, "*.dll", SearchOption.AllDirectories).Count();
            }
 
            if (dllCount == 0)
            {
                Log.WriteLine("Assets file contents:");
                Log.WriteLine(File.ReadAllText(Path.Combine(rootPath, "obj", "project.assets.json")));
            }
 
            Directory.Exists(fullPath).Should().BeTrue();
            dllCount.Should().BeGreaterThan(0);
        }
 
        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        public void ItRestoresTestAppToSpecificDirectory(bool useStaticGraphEvaluation)
        {
            var rootPath = _testAssetsManager.CopyTestAsset("VSTestCore", identifier: useStaticGraphEvaluation.ToString())
                .WithSource()
                .WithVersionVariables()
                .Path;
 
            string dir = "pkgs";
            string fullPath = Path.GetFullPath(Path.Combine(rootPath, dir));
 
            string[] args = new[] { "--packages", dir };
            args = HandleStaticGraphEvaluation(useStaticGraphEvaluation, args);
            new DotnetRestoreCommand(Log)
                .WithWorkingDirectory(rootPath)
                .Execute(args)
                .Should()
                .Pass()
                .And.NotHaveStdErr();
 
            Directory.Exists(fullPath).Should().BeTrue();
            Directory.EnumerateFiles(fullPath, "*.dll", SearchOption.AllDirectories).Count().Should().BeGreaterThan(0);
        }
 
        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        public void ItRestoresWithTheSpecifiedVerbosity(bool useStaticGraphEvaluation)
        {
            var rootPath = _testAssetsManager.CreateTestDirectory(identifier: useStaticGraphEvaluation.ToString()).Path;
 
            string dir = "pkgs";
            string fullPath = Path.GetFullPath(Path.Combine(rootPath, dir));
 
            string[] newArgs = new[] { "console", "-o", rootPath, "--no-restore" };
            new DotnetNewCommand(Log)
                .WithVirtualHive()
                .WithWorkingDirectory(rootPath)
                .Execute(newArgs)
                .Should()
                .Pass();
 
            string[] args = new[] { "--packages", dir, "--verbosity", "quiet" };
            args = HandleStaticGraphEvaluation(useStaticGraphEvaluation, args);
            new DotnetRestoreCommand(Log)
                 .WithWorkingDirectory(rootPath)
                 .Execute(args)
                 .Should()
                 .Pass()
                 .And.NotHaveStdErr()
                 .And.NotHaveStdOut();
        }
 
        [Fact]
        public void ItAcceptsArgumentsAfterProperties()
        {
            var rootPath = _testAssetsManager.CreateTestDirectory().Path;
 
            string[] newArgs = new[] { "console", "-o", rootPath, "--no-restore" };
            new DotnetNewCommand(Log)
                .WithVirtualHive()
                .WithWorkingDirectory(rootPath)
                .Execute(newArgs)
                .Should()
                .Pass();
 
            string[] args = new[] { "/p:prop1=true", "/m:1" };
            new DotnetRestoreCommand(Log)
                 .WithWorkingDirectory(rootPath)
                 .Execute(args)
                 .Should()
                 .Pass();
        }
        
        /// <summary>
        /// Tests for RID-specific restore options: -r/--runtime, --os, and -a/--arch
        /// </summary>
        [Theory]
        [InlineData("-r", "linux-x64")]
        [InlineData("--runtime", "win-x64")]
        [InlineData("--os", "linux")]
        [InlineData("-a", "arm64")]
        [InlineData("--arch", "x64")]
        [InlineData("--os", "linux", "-a", "arm64")]
        public void ItRestoresWithRidSpecificOptions(params string[] ridOptions)
        {
            // Skip test for #24251
            var testProject = new TestProject()
            {
                Name = "RestoreWithRidOptions",
                TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
            };
 
            testProject.PackageReferences.Add(new TestPackageReference("Newtonsoft.Json", ToolsetInfo.GetNewtonsoftJsonPackageVersion()));
            
            var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: string.Join("_", ridOptions));
            
            var rootPath = Path.Combine(testAsset.TestRoot, testProject.Name);
 
            // Create the command with the RID-specific options
            var restoreCommand = new DotnetRestoreCommand(Log)
                .WithWorkingDirectory(rootPath)
                .Execute(ridOptions);
 
            // Verify that the command runs successfully
            restoreCommand.Should().Pass();
            
            // Verify that assets file was created
            var assetsFilePath = Path.Combine(rootPath, "obj", "project.assets.json");
            File.Exists(assetsFilePath).Should().BeTrue();
 
            // Verify that the assets file contains the expected RID-specific target
            var assetsContents = JObject.Parse(File.ReadAllText(assetsFilePath));
            var targets = assetsContents["targets"];
            targets.Should().NotBeNull("assets file should contain targets section");
            
            // Determine the expected RID based on the options provided
            string expectedRid = GetExpectedRid(ridOptions);
            string expectedTarget = $"{ToolsetInfo.CurrentTargetFramework}/{expectedRid}";
            
            // Check that the specific target exists
            var specificTarget = targets[expectedTarget];
            specificTarget.Should().NotBeNull($"assets file should contain target '{expectedTarget}' when using RID options: {string.Join(" ", ridOptions)}");
        }
 
        private static string GetExpectedRid(string[] ridOptions)
        {
            // Check if explicit runtime is provided
            for (int i = 0; i < ridOptions.Length; i++)
            {
                if ((ridOptions[i] == "-r" || ridOptions[i] == "--runtime") && i + 1 < ridOptions.Length)
                {
                    return ridOptions[i + 1];
                }
            }
 
            // Get current platform defaults
            string currentOs = GetCurrentOsPart();
            string currentArch = GetCurrentArchPart();
 
            // Check for --os and --arch options to synthesize RID
            string targetOs = currentOs;
            string targetArch = currentArch;
 
            for (int i = 0; i < ridOptions.Length; i++)
            {
                if (ridOptions[i] == "--os" && i + 1 < ridOptions.Length)
                {
                    targetOs = ridOptions[i + 1];
                }
                else if ((ridOptions[i] == "-a" || ridOptions[i] == "--arch") && i + 1 < ridOptions.Length)
                {
                    targetArch = ridOptions[i + 1];
                }
            }
 
            return $"{targetOs}-{targetArch}";
        }
 
        private static string GetCurrentOsPart()
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                return "win";
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                return "linux";
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                return "osx";
            else
                throw new PlatformNotSupportedException("Unsupported platform for RID determination");
        }
 
        private static string GetCurrentArchPart()
        {
            return RuntimeInformation.OSArchitecture switch
            {
                Architecture.X64 => "x64",
                Architecture.X86 => "x86",
                Architecture.Arm64 => "arm64",
                Architecture.Arm => "arm",
                _ => throw new PlatformNotSupportedException($"Unsupported architecture: {RuntimeInformation.OSArchitecture}")
            };
        }
 
        private static string[] HandleStaticGraphEvaluation(bool useStaticGraphEvaluation, string[] args) =>
            useStaticGraphEvaluation ?
                args.Append("/p:RestoreUseStaticGraphEvaluation=true").ToArray() :
                args;
    }
}