File: Definition\Toolset_Tests.cs
Web Access
Project: ..\..\..\src\Build.UnitTests\Microsoft.Build.Engine.UnitTests.csproj (Microsoft.Build.Engine.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.Collections.Generic;
using System.Linq;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Collections;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
using Microsoft.Build.UnitTests.BackEnd;
using Xunit;
using Xunit.NetCore.Extensions;
 
#nullable disable
 
namespace Microsoft.Build.UnitTests.Definition
{
    public class Toolset_Tests
    {
        [Fact]
        public void ToolsetCtorErrors1()
        {
            Assert.Throws<ArgumentNullException>(() =>
            {
                Toolset t = new Toolset(null, "x", new ProjectCollection(), null);
            });
        }
 
        [Fact]
        public void ToolsetCtorErrors2()
        {
            Assert.Throws<ArgumentNullException>(() =>
            {
                Toolset t = new Toolset("x", null, new ProjectCollection(), null);
            });
        }
 
        [Fact]
        public void ToolsetCtorErrors3()
        {
            Assert.Throws<ArgumentException>(() =>
            {
                Toolset t = new Toolset(String.Empty, "x", new ProjectCollection(), null);
            });
        }
 
        [Fact]
        public void Regress27993_TrailingSlashTrimmedFromMSBuildToolsPath()
        {
            Toolset t;
            (string, string)[] testCases = null;
 
            if (NativeMethodsShared.IsWindows)
            {
                testCases = [
                    ("C:", @"C:"),
                    (@"C:\", @"C:\"),
                    (@"C:\\", @"C:\"),
                    (@"C:\foo", @"C:\foo"),
                    (@"C:\foo\", @"C:\foo"),
                    (@"C:\foo\\", @"C:\foo\"), // trim at most one slash
                    (@"\\foo\share", @"\\foo\share"),
                    (@"\\foo\share\", @"\\foo\share"),
                    (@"\\foo\share\\", @"\\foo\share\"), // trim at most one slash
                ];
            }
            else
            {
                testCases = [
                    ("/", "/"),
                    ("/foo", "/foo"),
                    ("/foo/", "/foo"),
                    ("/foo//", "/foo/"),
                    (@"\\foo\share", @"\\foo\share"),
                    (@"\\foo\share/", @"\\foo\share"),
                    (@"\\foo\share//", @"\\foo\share/"),
                ];
            }
            foreach ((string pathValue, string expectedPath) in testCases)
            {
                using var collection = new ProjectCollection();
                t = new Toolset("x", pathValue, collection, null);
                Assert.Equal(expectedPath, t.ToolsPath);
            }
        }
 
        [Fact]
        public void ValidateToolsetTranslation()
        {
            PropertyDictionary<ProjectPropertyInstance> buildProperties = new PropertyDictionary<ProjectPropertyInstance>();
            buildProperties.Set(ProjectPropertyInstance.Create("a", "a1"));
 
            PropertyDictionary<ProjectPropertyInstance> environmentProperties = new PropertyDictionary<ProjectPropertyInstance>();
            environmentProperties.Set(ProjectPropertyInstance.Create("b", "b1"));
 
            PropertyDictionary<ProjectPropertyInstance> globalProperties = new PropertyDictionary<ProjectPropertyInstance>();
            globalProperties.Set(ProjectPropertyInstance.Create("c", "c1"));
 
            PropertyDictionary<ProjectPropertyInstance> subToolsetProperties = new PropertyDictionary<ProjectPropertyInstance>();
            subToolsetProperties.Set(ProjectPropertyInstance.Create("d", "d1"));
 
            Dictionary<string, SubToolset> subToolsets = new Dictionary<string, SubToolset>(StringComparer.OrdinalIgnoreCase);
            subToolsets.Add("dogfood", new SubToolset("dogfood", subToolsetProperties));
 
            Toolset t = new Toolset("4.0", "c:\\bar", buildProperties, environmentProperties, globalProperties,
                subToolsets, "c:\\foo", "4.0", new Dictionary<string, ProjectImportPathMatch>
                {
                    ["MSBuildExtensionsPath"] = new ProjectImportPathMatch("MSBuildExtensionsPath", new List<string> { @"c:\foo" })
                });
 
            ((ITranslatable)t).Translate(TranslationHelpers.GetWriteTranslator());
            Toolset t2 = Toolset.FactoryForDeserialization(TranslationHelpers.GetReadTranslator());
 
            Assert.Equal(t.ToolsVersion, t2.ToolsVersion);
            Assert.Equal(t.ToolsPath, t2.ToolsPath);
            Assert.Equal(t.OverrideTasksPath, t2.OverrideTasksPath);
            Assert.Equal(t.Properties.Count, t2.Properties.Count);
 
            foreach (string key in t.Properties.Values.Select(p => p.Name))
            {
                Assert.Equal(t.Properties[key].Name, t2.Properties[key].Name);
                Assert.Equal(t.Properties[key].EvaluatedValue, t2.Properties[key].EvaluatedValue);
            }
 
            Assert.Equal(t.SubToolsets.Count, t2.SubToolsets.Count);
 
            foreach (string key in t.SubToolsets.Keys)
            {
                SubToolset subToolset1 = t.SubToolsets[key];
                SubToolset subToolset2 = null;
 
                if (t2.SubToolsets.TryGetValue(key, out subToolset2))
                {
                    Assert.Equal(subToolset1.SubToolsetVersion, subToolset2.SubToolsetVersion);
                    Assert.Equal(subToolset1.Properties.Count, subToolset2.Properties.Count);
 
                    foreach (string subToolsetPropertyKey in subToolset1.Properties.Values.Select(p => p.Name))
                    {
                        Assert.Equal(subToolset1.Properties[subToolsetPropertyKey].Name, subToolset2.Properties[subToolsetPropertyKey].Name);
                        Assert.Equal(subToolset1.Properties[subToolsetPropertyKey].EvaluatedValue, subToolset2.Properties[subToolsetPropertyKey].EvaluatedValue);
                    }
                }
                else
                {
                    Assert.Fail($"Sub-toolset {key} was lost in translation.");
                }
            }
 
            Assert.Equal(t.DefaultOverrideToolsVersion, t2.DefaultOverrideToolsVersion);
 
            Assert.NotNull(t2.ImportPropertySearchPathsTable);
            Assert.Single(t2.ImportPropertySearchPathsTable);
            Assert.Equal(@"c:\foo", t2.ImportPropertySearchPathsTable["MSBuildExtensionsPath"].SearchPaths[0]);
        }
 
        [Fact]
        public void TestDefaultSubToolset()
        {
            Toolset t = GetFakeToolset(null /* no global properties */);
 
            // The highest one numerically -- in this case, v13.
            Assert.Equal("v13.0", t.DefaultSubToolsetVersion);
        }
 
        [WindowsOnlyFact(Skip = "https://github.com/dotnet/msbuild/issues/4363")]
        public void TestDefaultSubToolsetFor40()
        {
            Toolset t = ProjectCollection.GlobalProjectCollection.GetToolset("4.0");
 
            if (t != null)
            {
                if (Toolset.Dev10IsInstalled)
                {
                    // If Dev10 is installed, the default sub-toolset = no sub-toolset
                    Assert.Equal(Constants.Dev10SubToolsetValue, t.DefaultSubToolsetVersion);
                }
                else
                {
                    // Otherwise, it's the highest one numerically.  Since by definition if Dev10 isn't
                    // installed and subtoolsets exists we must be at least Dev11, it should be "11.0"
                    Assert.Equal("11.0", t.DefaultSubToolsetVersion);
                }
            }
        }
 
        [Fact(Skip = "https://github.com/dotnet/msbuild/issues/4363")]
        public void TestDefaultWhenNoSubToolset()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", null);
 
                using ProjectCollection projectCollection = new ProjectCollection();
                Toolset parentToolset = projectCollection.GetToolset(ObjectModelHelpers.MSBuildDefaultToolsVersion);
 
                Toolset t = new Toolset("Fake", parentToolset.ToolsPath, null, projectCollection, null, parentToolset.OverrideTasksPath);
 
                if (Toolset.Dev10IsInstalled)
                {
                    Assert.Equal(Constants.Dev10SubToolsetValue, t.DefaultSubToolsetVersion);
                }
                else
                {
                    Assert.Null(t.DefaultSubToolsetVersion);
                }
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact(Skip = "https://github.com/dotnet/msbuild/issues/4363")]
        public void TestGenerateSubToolsetVersionWhenNoSubToolset()
        {
            if (NativeMethodsShared.IsUnixLike)
            {
                return; // "TODO: Under Unix this test throws. Investigate"
            }
 
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", null);
 
                using ProjectCollection projectCollection = new ProjectCollection();
                Toolset parentToolset = projectCollection.GetToolset(ObjectModelHelpers.MSBuildDefaultToolsVersion);
 
                Toolset t = new Toolset("Fake", parentToolset.ToolsPath, null, projectCollection, null, parentToolset.OverrideTasksPath);
 
                string subToolsetVersion = t.GenerateSubToolsetVersion();
 
                if (Toolset.Dev10IsInstalled)
                {
                    Assert.Equal(Constants.Dev10SubToolsetValue, subToolsetVersion);
                }
                else
                {
                    Assert.Null(subToolsetVersion);
                }
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestNoSubToolset_GlobalPropertyOverrides()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", null);
 
                IDictionary<string, string> globalProperties = new Dictionary<string, string>();
                globalProperties.Add("VisualStudioVersion", "99.0");
 
                using ProjectCollection projectCollection = new ProjectCollection(globalProperties);
                Toolset parentToolset = projectCollection.GetToolset(ObjectModelHelpers.MSBuildDefaultToolsVersion);
 
                Toolset t = new Toolset("Fake", parentToolset.ToolsPath, null, projectCollection, null, parentToolset.OverrideTasksPath);
 
                Assert.Equal("99.0", t.GenerateSubToolsetVersion());
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestNoSubToolset_EnvironmentOverrides()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", "foo");
 
                using ProjectCollection projectCollection = new ProjectCollection();
                Toolset parentToolset = projectCollection.GetToolset(ObjectModelHelpers.MSBuildDefaultToolsVersion);
 
                Toolset t = new Toolset("Fake", parentToolset.ToolsPath, null, projectCollection, null, parentToolset.OverrideTasksPath);
 
                Assert.Equal("foo", t.GenerateSubToolsetVersion());
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestNoSubToolset_ExplicitlyPassedGlobalPropertyOverrides()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", null);
 
                using ProjectCollection projectCollection = new ProjectCollection();
                Toolset parentToolset = projectCollection.GetToolset(ObjectModelHelpers.MSBuildDefaultToolsVersion);
 
                Toolset t = new Toolset("Fake", parentToolset.ToolsPath, null, projectCollection, null, parentToolset.OverrideTasksPath);
 
                IDictionary<string, string> globalProperties = new Dictionary<string, string>();
                globalProperties.Add("VisualStudioVersion", "v14.0");
 
                Assert.Equal("v14.0", t.GenerateSubToolsetVersion(globalProperties, 0));
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestNoSubToolset_ExplicitlyPassedGlobalPropertyWins()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", "foo");
 
                IDictionary<string, string> globalProperties = new Dictionary<string, string>();
                globalProperties.Add("VisualStudioVersion", "v13.0");
 
                using ProjectCollection projectCollection = new ProjectCollection(globalProperties);
                Toolset parentToolset = projectCollection.GetToolset(ObjectModelHelpers.MSBuildDefaultToolsVersion);
 
                Toolset t = new Toolset("Fake", parentToolset.ToolsPath, null, projectCollection, null, parentToolset.OverrideTasksPath);
 
                IDictionary<string, string> explicitGlobalProperties = new Dictionary<string, string>();
                explicitGlobalProperties.Add("VisualStudioVersion", "baz");
 
                Assert.Equal("baz", t.GenerateSubToolsetVersion(explicitGlobalProperties, 0));
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestGenerateSubToolsetVersion_GlobalPropertyOverrides()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", null);
 
                IDictionary<string, string> globalProperties = new Dictionary<string, string>();
                globalProperties.Add("VisualStudioVersion", ObjectModelHelpers.CurrentVisualStudioVersion);
 
                Toolset t = GetFakeToolset(globalProperties);
 
                Assert.Equal(ObjectModelHelpers.CurrentVisualStudioVersion, t.GenerateSubToolsetVersion());
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestGenerateSubToolsetVersion_EnvironmentOverrides()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", "FakeSubToolset");
 
                Toolset t = GetFakeToolset(null);
 
                Assert.Equal("FakeSubToolset", t.GenerateSubToolsetVersion());
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestGenerateSubToolsetVersion_ExplicitlyPassedGlobalPropertyOverrides()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", null);
 
                Toolset t = GetFakeToolset(null);
 
                IDictionary<string, string> globalProperties = new Dictionary<string, string>();
                globalProperties.Add("VisualStudioVersion", "v13.0");
 
                Assert.Equal("v13.0", t.GenerateSubToolsetVersion(globalProperties, 0));
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestGenerateSubToolsetVersion_SolutionVersionOverrides()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", null);
 
                Toolset t = GetFakeToolset(null);
 
                // VisualStudioVersion = SolutionVersion - 1
                Assert.Equal("12.0", t.GenerateSubToolsetVersion(null, 13));
                Assert.Equal("v13.0", t.GenerateSubToolsetVersion(null, 14));
 
                // however, if there is no matching solution version, we just fall back to the
                // default sub-toolset.
                Assert.Equal(t.DefaultSubToolsetVersion, t.GenerateSubToolsetVersion(null, 55));
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestGenerateSubToolsetVersion_ExplicitlyPassedGlobalPropertyWins()
        {
            string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            try
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", ObjectModelHelpers.CurrentVisualStudioVersion);
 
                IDictionary<string, string> globalProperties = new Dictionary<string, string>();
                globalProperties.Add("VisualStudioVersion", "v13.0");
 
                using ProjectCollection projectCollection = new ProjectCollection(globalProperties);
                Toolset parentToolset = projectCollection.GetToolset(ObjectModelHelpers.MSBuildDefaultToolsVersion);
 
                Toolset t = new Toolset("Fake", parentToolset.ToolsPath, null, projectCollection, null, parentToolset.OverrideTasksPath);
 
                IDictionary<string, string> explicitGlobalProperties = new Dictionary<string, string>();
                explicitGlobalProperties.Add("VisualStudioVersion", "FakeSubToolset");
 
                Assert.Equal("FakeSubToolset", t.GenerateSubToolsetVersion(explicitGlobalProperties, 0));
            }
            finally
            {
                Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion);
            }
        }
 
        [Fact]
        public void TestGetPropertyFromSubToolset()
        {
            Toolset t = GetFakeToolset(null);
 
            Assert.Equal("a1", t.GetProperty("a", "v11.0").EvaluatedValue); // property in base toolset
            Assert.Equal("c2", t.GetProperty("c", "v11.0").EvaluatedValue); // property in sub-toolset
            Assert.Equal("b2", t.GetProperty("b", "v11.0").EvaluatedValue); // property in sub-toolset that overrides base toolset
            Assert.Null(t.GetProperty("d", "v11.0")); // property in a different sub-toolset
        }
 
        /// <summary>
        /// Creates a standard ProjectCollection and adds a fake toolset with the following contents to it:
        ///
        /// ToolsVersion = Fake
        /// Base Properties:
        /// a = a1
        /// b = b1
        ///
        /// SubToolset "12.0":
        /// d = d4
        /// e = e5
        ///
        /// SubToolset "v11.0":
        /// b = b2
        /// c = c2
        ///
        /// SubToolset "FakeSubToolset":
        /// a = a3
        /// c = c3
        ///
        /// SubToolset "v13.0":
        /// f = f6
        /// g = g7
        /// </summary>
        private Toolset GetFakeToolset(IDictionary<string, string> globalPropertiesForProjectCollection)
        {
            using ProjectCollection projectCollection = new ProjectCollection(globalPropertiesForProjectCollection);
 
            IDictionary<string, string> properties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            properties.Add("a", "a1");
            properties.Add("b", "b1");
 
            Dictionary<string, SubToolset> subToolsets = new Dictionary<string, SubToolset>(StringComparer.OrdinalIgnoreCase);
 
            // SubToolset 12.0 properties
            PropertyDictionary<ProjectPropertyInstance> subToolset12Properties = new PropertyDictionary<ProjectPropertyInstance>();
            subToolset12Properties.Set(ProjectPropertyInstance.Create("d", "d4"));
            subToolset12Properties.Set(ProjectPropertyInstance.Create("e", "e5"));
 
            // SubToolset v11.0 properties
            PropertyDictionary<ProjectPropertyInstance> subToolset11Properties = new PropertyDictionary<ProjectPropertyInstance>();
            subToolset11Properties.Set(ProjectPropertyInstance.Create("b", "b2"));
            subToolset11Properties.Set(ProjectPropertyInstance.Create("c", "c2"));
 
            // FakeSubToolset properties
            PropertyDictionary<ProjectPropertyInstance> fakeSubToolsetProperties = new PropertyDictionary<ProjectPropertyInstance>();
            fakeSubToolsetProperties.Set(ProjectPropertyInstance.Create("a", "a3"));
            fakeSubToolsetProperties.Set(ProjectPropertyInstance.Create("c", "c3"));
 
            // SubToolset v13.0 properties
            PropertyDictionary<ProjectPropertyInstance> subToolset13Properties = new PropertyDictionary<ProjectPropertyInstance>();
            subToolset13Properties.Set(ProjectPropertyInstance.Create("f", "f6"));
            subToolset13Properties.Set(ProjectPropertyInstance.Create("g", "g7"));
 
            subToolsets.Add("12.0", new SubToolset("12.0", subToolset12Properties));
            subToolsets.Add("v11.0", new SubToolset("v11.0", subToolset11Properties));
            subToolsets.Add("FakeSubToolset", new SubToolset("FakeSubToolset", fakeSubToolsetProperties));
            subToolsets.Add("v13.0", new SubToolset("v13.0", subToolset13Properties));
 
            Toolset parentToolset = projectCollection.GetToolset(ObjectModelHelpers.MSBuildDefaultToolsVersion);
 
            Toolset fakeToolset = new Toolset("Fake", parentToolset.ToolsPath, properties, projectCollection, subToolsets, parentToolset.OverrideTasksPath);
 
            projectCollection.AddToolset(fakeToolset);
 
            return fakeToolset;
        }
    }
}