File: Definition\ToolsetReader_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.Configuration;
using System.IO;
 
using Microsoft.Build.Collections;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
using Microsoft.Win32;
 
#if FEATURE_WIN32_REGISTRY
using RegistryKeyWrapper = Microsoft.Build.Internal.RegistryKeyWrapper;
using RegistryException = Microsoft.Build.Exceptions.RegistryException;
#endif
using InvalidToolsetDefinitionException = Microsoft.Build.Exceptions.InvalidToolsetDefinitionException;
using InternalUtilities = Microsoft.Build.Internal.Utilities;
using Xunit;
using Xunit.NetCore.Extensions;
 
#nullable disable
 
namespace Microsoft.Build.UnitTests.Definition
{
#if FEATURE_REGISTRY_TOOLSETS

    /// <summary>
    /// Unit tests for ToolsetReader class and its derived classes
    /// </summary>
    public class ToolsetReaderTests : IDisposable
    {
        // The registry key that is passed as the baseKey parameter to the ToolsetRegistryReader class
        private RegistryKey _testRegistryKey = null;
        // Subkey "4.0"
        private RegistryKey _currentVersionRegistryKey = null;
        // Subkey "ToolsVersions"
        private RegistryKey _toolsVersionsRegistryKey = null;
 
        // Path to the registry key under HKCU
        // Note that this is a test registry key created solely for unit testing.
        private const string testRegistryPath = @"msbuildUnitTests";
 
        /// <summary>
        /// Store the value of the "VisualStudioVersion" environment variable here so that
        /// we can unset it for the duration of the test.
        /// </summary>
        private string _oldVisualStudioVersion;
 
        /// <summary>
        /// Reset the testRegistryKey
        /// </summary>
        public ToolsetReaderTests()
        {
            Dispose();
            _testRegistryKey = Registry.CurrentUser.CreateSubKey(testRegistryPath);
            _currentVersionRegistryKey = Registry.CurrentUser.CreateSubKey(testRegistryPath + "\\" + Constants.AssemblyVersion);
            _toolsVersionsRegistryKey = Registry.CurrentUser.CreateSubKey(testRegistryPath + "\\ToolsVersions");
 
            _oldVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
 
            Environment.SetEnvironmentVariable("VisualStudioVersion", null);
        }
 
        public void Dispose()
        {
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.CleanUp();
#endif

            DeleteTestRegistryKey();
 
            Environment.SetEnvironmentVariable("VisualStudioVersion", _oldVisualStudioVersion);
        }
 
        /// <summary>
        /// Helper class to delete the testRegistryKey tree.
        /// </summary>
        private void DeleteTestRegistryKey()
        {
            if (Registry.CurrentUser.OpenSubKey(testRegistryPath) != null)
            {
                Registry.CurrentUser.DeleteSubKeyTree(testRegistryPath);
            }
        }
 
#if FEATURE_SYSTEM_CONFIGURATION
        /// <summary>
        /// Test to make sure machine.config file has the section registered
        /// and we are picking it up from there.
        /// </summary>
        [WindowsOnlyFact(additionalMessage: "The machine.config is only present on Windows.")]
        public void GetToolsetDataFromConfiguration_SectionNotRegisteredInConfigFile()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Null(msbuildOverrideTasksPath);
            Assert.Null(defaultToolsVersion);
            Assert.Empty(values);
        }
#endif

        #region "Reading from application configuration file tests"

#if FEATURE_SYSTEM_CONFIGURATION

        /// <summary>
        /// Tests that the data is correctly populated using function GetToolsetDataFromConfiguration
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_Basic()
        {
            string v2Folder = NativeMethodsShared.IsWindows
                                  ? @"D:\windows\Microsoft.NET\Framework\v2.0.x86ret"
                                  : Path.Combine(NativeMethodsShared.FrameworkBasePath, "2.0");
            string v4Folder = NativeMethodsShared.IsWindows
                                  ? @"D:\windows\Microsoft.NET\Framework\v4.0.x86ret"
                                  : Path.Combine(NativeMethodsShared.FrameworkBasePath, "4.0");
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"" msbuildOverrideTasksPath=""c:\Cat"" DefaultOverrideToolsVersion=""4.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""" + v2Folder + @"""/>
                     </toolset>
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""" + v4Folder + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Equal("c:\\Cat", msbuildOverrideTasksPath);
            Assert.Equal("4.0", defaultOverrideToolsVersion);
            Assert.Equal("2.0", defaultToolsVersion);
            Assert.Equal(2, values.Count);
            Assert.Empty(values["2.0"].Properties);
            Assert.Equal(v2Folder, values["2.0"].ToolsPath);
            Assert.Empty(values["4.0"].Properties);
            Assert.Equal(v4Folder, values["4.0"].ToolsPath);
        }
 
        /// <summary>
        /// Relative paths can be used in a config file value
        /// </summary>
        [WindowsOnlyFact]
        public void RelativePathInValue()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"" msbuildOverrideTasksPath=""..\Foo"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildToolsPath"" value="".." + Path.DirectorySeparatorChar + @"foo""/>
                       <!-- derelativization occurs before comparing toolspath and binpath -->
                       <property name=""MSBuildBinPath"" value="".." + Path.DirectorySeparatorChar + "." + Path.DirectorySeparatorChar + @"foo""/>
                     </toolset>
                     <toolset toolsVersion=""3.0"">
                       <!-- properties are expanded before derelativization-->
                       <property name=""MSBuildBinPath"" value=""$(DotDotSlash)bar""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            PropertyDictionary<ProjectPropertyInstance> pg = new PropertyDictionary<ProjectPropertyInstance>();
            pg.Set(ProjectPropertyInstance.Create("DotDotSlash", @".." + Path.DirectorySeparatorChar));
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), pg, true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            string expected1 = Path.GetFullPath(Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, "..", "foo"));
            string expected2 = Path.GetFullPath(Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, "..", "bar"));
            Console.WriteLine(values["2.0"].ToolsPath);
            Assert.Equal(expected1, values["2.0"].ToolsPath);
            Assert.Equal(expected2, values["3.0"].ToolsPath);
            Assert.Equal("..\\Foo", msbuildOverrideTasksPath);
        }
 
        /// <summary>
        /// Invalid relative path in msbuildbinpath value
        /// </summary>
        [WindowsOnlyFact]
        public void InvalidRelativePath()
        {
            if (NativeMethodsShared.IsLinux)
            {
                return; // "Cannot force invalid character name on Linux"
            }
 
            string invalidRelativePath = ".." + Path.DirectorySeparatorChar + ":|invalid|";
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"" msbuildOverrideTasksPath="""">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""" + invalidRelativePath + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            // Don't crash (consistent with invalid absolute path)
            Assert.Equal(invalidRelativePath, values["2.0"].ToolsPath);
            Assert.Null(msbuildOverrideTasksPath);
        }
 
        /// <summary>
        /// Tests the case where application configuration file is invalid
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_InvalidXmlFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"<INVALIDXML>");
 
                ToolsetReader reader = GetStandardConfigurationReader();
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
        /// <summary>
        /// Tests the case where application configuration file is invalid
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_InvalidConfigFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolsVersion>
                     <SOMEINVALIDTAG/>
                   </msbuildToolsets>
                 </configuration>");
 
                ToolsetReader reader = GetStandardConfigurationReader();
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
        /// <summary>
        /// Tests the case where application configuration file is empty
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_FileEmpty()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"");
 
                ToolsetReader reader = new ToolsetConfigurationReader(new ProjectCollection().EnvironmentProperties, new PropertyDictionary<ProjectPropertyInstance>(), ToolsetConfigurationReaderTestHelper.ReadApplicationConfigurationTest);
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
        /// <summary>
        /// Tests the case when ReadConfiguration throws exception
        /// Make sure that we don't eat it and always throw ConfigurationErrorsException
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_ConfigurationExceptionThrown()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"", new ConfigurationErrorsException());
 
                ToolsetReader reader = GetStandardConfigurationReader();
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // this should throw ...
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
        /// <summary>
        /// Tests the case when ReadConfiguration throws exception
        /// Make sure that we don't eat it and always throw ConfigurationErrorsException
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_ConfigurationErrorsExceptionThrown()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"", new ConfigurationErrorsException());
 
                ToolsetReader reader = GetStandardConfigurationReader();
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // this should throw ...
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
        /// <summary>
        /// Tests the case where default attribute is not specified in the config file
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_DefaultAttributeNotSpecified()
        {
            string v2Folder = NativeMethodsShared.IsWindows
                                  ? @"D:\windows\Microsoft.NET\Framework\v2.0.x86ret"
                                  : Path.Combine(NativeMethodsShared.FrameworkBasePath, "2.0");
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets msbuildOverrideTasksPath=""C:\Cat"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""" + v2Folder + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Null(defaultToolsVersion);
            Assert.Single(values);
            Assert.Empty(values["2.0"].Properties);
            Assert.Equal(v2Folder, values["2.0"].ToolsPath);
            Assert.Equal("C:\\Cat", msbuildOverrideTasksPath);
        }
 
        /// <summary>
        /// Default toolset has no toolsVersion element definition
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_DefaultToolsetUndefined()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""nonexistent"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
 
            // Should not throw
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
        }
 
        /// <summary>
        /// Tests the case where msbuildToolsets is not specified in the config file
        /// Basically in the code we should be checking if config.GetSection("msbuildToolsets") returns a null
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_MSBuildToolsetsNodeNotPresent()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Null(defaultToolsVersion);
            Assert.Empty(values);
        }
 
        /// <summary>
        /// Tests that we handle empty MSBuildToolsets element correctly
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_EmptyMSBuildToolsetsNode()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets/>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Null(defaultToolsVersion);
            Assert.Empty(values);
        }
 
        /// <summary>
        /// Tests the case where only default ToolsVersion is specified in the application configuration file
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_OnlyDefaultSpecified()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0""/>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
 
            // Should not throw
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Empty(values);
        }
 
        /// <summary>
        /// Tests the case where only one ToolsVersion data is specified in the application configuration file
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_OneToolsVersionNode()
        {
            string v2Folder = NativeMethodsShared.IsWindows
                                  ? @"D:\windows\Microsoft.NET\Framework\v2.0.x86ret"
                                  : Path.Combine(NativeMethodsShared.FrameworkBasePath, "2.0");
 
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""" + v2Folder + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Equal("2.0", defaultToolsVersion);
            Assert.Equal(v2Folder, values["2.0"].ToolsPath);
            Assert.Single(values);
        }
 
        /// <summary>
        /// Tests the case when an invalid value of ToolsVersion is specified
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_ToolsVersionIsEmptyString()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion="""">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                ToolsetReader reader = GetStandardConfigurationReader();
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // this should throw ...
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
        /// <summary>
        /// If both MSBuildToolsPath and MSBuildBinPath are present, they must match
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_ToolsPathAndBinPathDiffer()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                       <property name=""MSBuildToolsPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                ToolsetReader reader = GetStandardConfigurationReader();
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
        /// <summary>
        /// Tests the case when a blank value of PropertyName is specified in the config file
        /// </summary>
        [WindowsOnlyFact]
        public void BlankPropertyNameInConfigFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                       <property name="""" value=""foo""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                ToolsetReader reader = GetStandardConfigurationReader();
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // this should throw ...
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
#endif

#if FEATURE_WIN32_REGISTRY
        /// <summary>
        /// Tests the case when a blank property name is specified in the registry
        /// </summary>
        [WindowsOnlyFact]
        public void BlankPropertyNameInRegistry()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                rk.SetValue("MSBuildBinPath", "someBinPath");
                rk.SetValue("", "foo");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // Should throw ...
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
        /// <summary>
        /// Tests the case when a blank property name is specified in the registry in a
        /// sub-toolset.
        /// </summary>
        [WindowsOnlyFact]
        public void BlankPropertyNameInRegistrySubToolset()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                rk.SetValue("MSBuildBinPath", "someBinPath");
 
                RegistryKey subToolsetKey = rk.CreateSubKey("11.0");
                subToolsetKey.SetValue("", "foo");
 
                PropertyDictionary<ProjectPropertyInstance> globalProperties = new PropertyDictionary<ProjectPropertyInstance>();
                globalProperties.Set(ProjectPropertyInstance.Create("VisualStudioVersion", "11.0"));
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // Should throw ...
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           new ProjectCollection().EnvironmentProperties,
                                                               globalProperties,
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
#endif
#if FEATURE_SYSTEM_CONFIGURATION
        /// <summary>
        /// Tests the case when a blank property value is specified in the config file
        /// </summary>
        [WindowsOnlyFact]
        public void BlankPropertyValueInConfigFile()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                       <property name=""foo"" value=""""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
 
            // this should not throw ...
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
        }
#endif

        /// <summary>
        /// Tests the case when a blank property value is specified in the registry
        /// </summary>
        [WindowsOnlyFact]
        public void BlankPropertyValueInRegistry()
        {
            RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            rk.SetValue("MSBuildBinPath", "someBinPath");
            rk.SetValue("foo", "");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            // Should not throw ...
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Registry);
        }
 
        /// <summary>
        /// Tests the case when a blank property value is specified in the registry
        /// </summary>
        [WindowsOnlyFact]
        public void BlankPropertyValueInRegistrySubToolset()
        {
            string binPath = NativeMethodsShared.IsWindows ? @"c:\someBinPath" : "/someBinPath";
            RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            rk.SetValue("MSBuildBinPath", binPath);
 
            RegistryKey subToolsetKey = rk.CreateSubKey("11.0");
            subToolsetKey.SetValue("foo", "");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            // Should not throw ...
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Registry);
 
            Assert.Equal("2.0", defaultToolsVersion);
            Assert.Empty(values["2.0"].Properties);
            Assert.Equal(binPath, values["2.0"].ToolsPath);
            Assert.Single(values["2.0"].SubToolsets);
            Assert.Equal("", values["2.0"].SubToolsets["11.0"].Properties["foo"].EvaluatedValue);
        }
 
#if FEATURE_SYSTEM_CONFIGURATION
        /// <summary>
        /// Tests the case when an invalid value of PropertyName is specified in the config file
        /// </summary>
        [WindowsOnlyFact]
        public void InvalidPropertyNameInConfigFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                       <property name=""&amp;"" value=""foo""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                ToolsetReader reader = GetStandardConfigurationReader();
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // this should throw ...
                string msbuildOverrideTasksPath = null;
                string defaultOverrideToolsVersion = null;
                reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
            });
        }
#endif

        /// <summary>
        /// Tests the case when an invalid value of PropertyName is specified in the registry
        /// </summary>
        [WindowsOnlyFact]
        public void InvalidPropertyNameInRegistry()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                rk.SetValue("MSBuildBinPath", "someBinPath");
                rk.SetValue("foo|bar", "x");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // Should throw ...
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                               GetStandardConfigurationReader(),
#endif
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
        /// <summary>
        /// Tests the case when an invalid value of PropertyName is specified in the registry
        /// </summary>
        [WindowsOnlyFact]
        public void InvalidPropertyNameInRegistrySubToolset()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
 
                RegistryKey subToolsetKey = rk.CreateSubKey("10.0");
                subToolsetKey.SetValue("MSBuildBinPath", "someBinPath");
                subToolsetKey.SetValue("foo|bar", "x");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // Should throw ...
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
 
#if FEATURE_SYSTEM_CONFIGURATION
        /// <summary>
        /// Tests that empty string is an invalid value for MSBuildBinPath
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_PropertyValueIsEmptyString1()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Empty(values);
        }
 
        /// <summary>
        /// Tests that empty string is a valid property value for an arbitrary property
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_PropertyValueIsEmptyString2()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildToolsPath"" value=""xxx""/>
                       <property name=""foo"" value=""""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Single(values);
            Assert.Single(values["2.0"].Properties);
            Assert.Equal(String.Empty, values["2.0"].Properties["foo"].EvaluatedValue);
        }
 
        /// <summary>
        /// Tests that any escaped xml in config file, is treated well
        /// Note that this comes for free with the current implementation using the
        /// framework api to access section in the config file
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetDataFromConfiguration_XmlEscapedCharacters()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2&gt;.0"">
                     <toolset toolsVersion=""2&gt;.0"">
                       <property name=""MSBuildBinPath"" value=""x""/>
                       <property name=""foo"" value=""some&gt;value""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            ToolsetReader reader = GetStandardConfigurationReader();
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            string msbuildOverrideTasksPath;
            string defaultOverrideToolsVersion;
            string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), true, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion);
 
            Assert.Equal("2>.0", defaultToolsVersion);
            Assert.Single(values);
            Assert.Equal(@"some>value", values["2>.0"].Properties["foo"].EvaluatedValue);
        }
#endif
#pragma warning disable format
    #endregion
#pragma warning restore format

        #region "GetToolsetData tests"

        /// <summary>
        /// Tests the case where registry and config file contains different toolsVersion
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_NoConflict()
        {
            string binPath = NativeMethodsShared.IsWindows ? @"D:\somepath" : "/somepath";
            string binPath2 = NativeMethodsShared.IsWindows ? @"D:\somepath2" : "/somepath2";
            string fworkPath2 = NativeMethodsShared.IsWindows
                                    ? @"D:\windows\Microsoft.NET\Framework\v2.0.x86ret"
                                    : "/windows/Microsoft.NET/Framework/v2.0.x86ret";
            string fworkPath4 = NativeMethodsShared.IsWindows
                                    ? @"D:\windows\Microsoft.NET\Framework\v4.0.x86ret"
                                    : "/windows/Microsoft.NET/Framework/v4.0.x86ret";
            // Set up registry with two tools versions and one property each
            _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "2.0");
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", binPath);
            RegistryKey key2 = _toolsVersionsRegistryKey.CreateSubKey("4.0");
            key2.SetValue("MSBuildBinPath", binPath2);
 
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""4.5"">
                     <toolset toolsVersion=""4.5"">
                       <property name=""MSBuildBinPath"" value=""" + fworkPath2 + Path.DirectorySeparatorChar + @"""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""" + fworkPath4 + Path.DirectorySeparatorChar + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            // Verifications
            Assert.Equal(4, values.Count);
            Assert.Equal("4.5", defaultToolsVersion);
            Assert.Equal(binPath, values["2.0"].ToolsPath);
            Assert.Equal(binPath2, values["4.0"].ToolsPath);
            Assert.Equal(fworkPath2, values["4.5"].ToolsPath);
            Assert.Equal(fworkPath4, values["5.0"].ToolsPath);
        }
 
        /// <summary>
        /// Tests that ToolsetInitialization are respected.
        /// </summary>
        [WindowsOnlyFact]
        public void ToolsetInitializationFlagsSetToNone()
        {
            // Set up registry with two tools versions and one property each
            _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "22.0");
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("33.0");
            key1.SetValue("MSBuildBinPath", @"D:\somepath");
            RegistryKey key2 = _testRegistryKey.CreateSubKey("55.0");
            key2.SetValue("MSBuildBinPath", @"D:\somepath2");
 
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""4.5"">
                     <toolset toolsVersion=""5.5"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""6.5"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.None);
 
            // Verifications
            Assert.Single(values);
 
            string expectedDefault = "2.0";
            if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null)
            {
                expectedDefault = ObjectModelHelpers.MSBuildDefaultToolsVersion;
            }
 
            Assert.Equal(expectedDefault, defaultToolsVersion);
        }
 
        /// <summary>
        /// Tests that ToolsetInitialization are respected.
        /// </summary>
        [WindowsOnlyFact]
        public void ToolsetInitializationFlagsSetToRegistry()
        {
            string binPath = NativeMethodsShared.IsWindows ? @"D:\somepath" : "/somepath";
            string binPath2 = NativeMethodsShared.IsWindows ? @"D:\somepath2" : "/somepath2";
            // Set up registry with two tools versions and one property each
            _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "2.0");
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", binPath);
            RegistryKey key2 = _toolsVersionsRegistryKey.CreateSubKey("4.0");
            key2.SetValue("MSBuildBinPath", binPath2);
 
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""4.5"">
                     <toolset toolsVersion=""4.5"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Registry);
 
            // Verifications
            Assert.Equal(2, values.Count);
            Assert.Equal("2.0", defaultToolsVersion);
            Assert.Equal(binPath, values["2.0"].ToolsPath);
            Assert.Equal(binPath2, values["4.0"].ToolsPath);
        }
 
        [WindowsOnlyFact]
        public void ThrowOnNonStringRegistryValueTypes()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                rk.SetValue("MSBuildBinPath", "someBinPath");
 
                // Non-string
                rk.SetValue("QuadWordValue", 42, RegistryValueKind.QWord);
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // Should throw ...
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                               GetStandardConfigurationReader(),
#endif
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
        [WindowsOnlyFact]
        public void PropertiesInRegistryCannotReferToOtherPropertiesInRegistry()
        {
            string binPath = NativeMethodsShared.IsWindows ? "c:\\x" : "/x";
            RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            rk.SetValue("MSBuildBinPath", binPath + "$(p1)");
            rk.SetValue("p0", "$(p1)");
            rk.SetValue("p1", "v");
            rk.SetValue("p2", "$(p1)");
            rk.SetValue("MSBuildToolsPath", binPath + "$(p1)");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Registry);
 
            Assert.Equal("", values["2.0"].Properties["p0"].EvaluatedValue);
            Assert.Equal("v", values["2.0"].Properties["p1"].EvaluatedValue);
            Assert.Equal("", values["2.0"].Properties["p2"].EvaluatedValue);
            Assert.Equal(binPath, values["2.0"].ToolsPath);
        }
 
        [WindowsOnlyFact]
        public void SubToolsetPropertiesInRegistryCannotReferToOtherPropertiesInRegistry()
        {
            RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            rk.SetValue("MSBuildBinPath", "c:\\x$(p1)");
            rk.SetValue("p0", "$(p1)");
            rk.SetValue("p1", "v");
 
            RegistryKey subToolsetKey = rk.CreateSubKey("dogfood");
            subToolsetKey.SetValue("p2", "$(p1)");
            subToolsetKey.SetValue("p3", "c:\\x$(p1)");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Registry);
 
            Assert.Equal("", values["2.0"].Properties["p0"].EvaluatedValue);
            Assert.Equal("v", values["2.0"].Properties["p1"].EvaluatedValue);
            Assert.Equal("", values["2.0"].SubToolsets["dogfood"].Properties["p2"].EvaluatedValue);
            Assert.Equal("c:\\x", values["2.0"].SubToolsets["dogfood"].Properties["p3"].EvaluatedValue);
        }
 
        [WindowsOnlyFact]
        public void SubToolsetsCannotDefineMSBuildToolsPath()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                RegistryKey rk = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                rk.SetValue("p0", "$(p1)");
                rk.SetValue("p1", "v");
 
                RegistryKey subToolsetKey = rk.CreateSubKey("dogfood");
                subToolsetKey.SetValue("p2", "$(p1)");
                subToolsetKey.SetValue("MSBuildToolsPath", "c:\\x$(p1)");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // throws
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                               GetStandardConfigurationReader(),
#endif
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
        /// <summary>
        /// Tests that ToolsetInitialization are respected.
        /// </summary>
        [WindowsOnlyFact]
        public void ToolsetInitializationFlagsSetToConfigurationFile()
        {
            string v2Dir = NativeMethodsShared.IsWindows ? "D:\\windows\\Microsoft.NET\\Framework\\v2.0.x86ret" : "/windows/Microsoft.NET/Framework/v2.0.x86ret";
            string v4Dir = NativeMethodsShared.IsWindows ? "D:\\windows\\Microsoft.NET\\Framework\\v4.0.x86ret" : "/windows/Microsoft.NET/Framework/v4.0.x86ret";
 
            // Set up registry with two tools versions and one property each
            _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "2.0");
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", @"D:\somepath");
            RegistryKey key2 = _toolsVersionsRegistryKey.CreateSubKey("4.0");
            key2.SetValue("MSBuildBinPath", @"D:\somepath2");
 
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""4.5"">
                     <toolset toolsVersion=""4.5"">
                       <property name=""MSBuildBinPath"" value=""" + v2Dir + @"""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""" + v4Dir + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.ConfigurationFile);
 
            // Verifications
            Assert.Equal(2, values.Count);
            Assert.Equal("4.5", defaultToolsVersion);
            Assert.Equal(v2Dir, values["4.5"].ToolsPath);
            Assert.Equal(v4Dir, values["5.0"].ToolsPath);
        }
 
        /// <summary>
        /// Properties in the configuration file may refer to a registry location by using the syntax for example
        /// "$(Registry:HKEY_LOCAL_MACHINE\Software\Vendor\Tools@TaskLocation)", where "HKEY_LOCAL_MACHINE\Software\Vendor\Tools" is the key and
        /// "TaskLocation" is the name of the value.  The name of the value and the preceding "@" may be omitted if
        /// the default value is desired.
        /// </summary>
        [WindowsOnlyFact(additionalMessage: "Registry access is only supported under Windows.")]
        public void PropertyInConfigurationFileReferencesRegistryLocation()
        {
            // Registry Read
            RegistryKey key1 = Registry.CurrentUser.CreateSubKey(@"Software\Vendor\Tools");
            key1.SetValue("TaskLocation", @"somePathToTasks");
            key1.SetValue("TargetsLocation", @"D:\somePathToTargets");
            key1.SetValue("SchemaLocation", @"Schemas");
            key1.SetValue(null, @"D:\somePathToDefault");  // this sets the default value for this key
 
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\$(Registry:HKEY_CURRENT_USER\Software\Vendor\Tools@TaskLocation)""/>
                       <property name=""p1"" value=""$(p2)$(REGISTRY:HKEY_CURRENT_USER\Software\Vendor\Tools)""/>
                       <property name=""p2"" value=""$(p1)\$(Registry:hkey_current_user\Software\Vendor\Tools@TaskLocation)\$(Registry:HKEY_CURRENT_USER\Software\Vendor\Tools@SchemaLocation)\2.0""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Single(values);
            Assert.Equal(@"D:\somePathToTasks", values["2.0"].ToolsPath);
            Assert.Equal(2, values["2.0"].Properties.Count);
            Assert.Equal(@"D:\somePathToDefault", values["2.0"].Properties["p1"].EvaluatedValue);
            Assert.Equal(@"D:\somePathToDefault\somePathToTasks\Schemas\2.0", values["2.0"].Properties["p2"].EvaluatedValue);
 
            Registry.CurrentUser.DeleteSubKeyTree(@"Software\Vendor");
        }
 
        [WindowsOnlyFact]
        public void ToolsPathInRegistryHasInvalidPathChars()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "2.0");
                RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                key1.SetValue("MSBuildBinPath", @"D:\some\foo|bar\path\");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                // should throw...
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                               null,
#endif
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
 
#if FEATURE_SYSTEM_CONFIGURATION
        [WindowsOnlyFact]
        public void SamePropertyDefinedMultipleTimesForSingleToolsVersionInConfigurationFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\some\folder\on\disk""/>
                       <property name=""p1"" value=""another""/>
                       <property name=""MSBuildBinPath"" value=""C:\$(p1)\folder""/>
                       <property name=""p1"" value=""newValue""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               null,
                                                               GetStandardConfigurationReader(),
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.ConfigurationFile);
            });
        }
 
        [WindowsOnlyFact]
        public void SamePropertyDifferentCaseDefinedMultipleTimesForSingleToolsVersionInConfigurationFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\some\folder\on\disk""/>
                       <property name=""p1"" value=""another""/>
                       <property name=""mSbUiLdBiNpAtH"" value=""C:\$(p1)\folder""/>
                       <property name=""P1"" value=""newValue""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               null,
                                                               GetStandardConfigurationReader(),
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.ConfigurationFile);
            });
        }
 
 
        [WindowsOnlyFact]
        public void SameToolsVersionDefinedMultipleTimesInConfigurationFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\some\folder\on\disk""/>
                       <property name=""p1"" value=""another""/>
                     </toolset>
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\folder""/>
                       <property name=""p2"" value=""anotherValue""/>
                     </toolset>
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\some\folder\on\disk""/>
                       <property name=""p1"" value=""another""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               null,
                                                               GetStandardConfigurationReader(),
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.ConfigurationFile);
            });
        }
 
        [WindowsOnlyFact]
        public void SameToolsVersionDifferentCaseDefinedMultipleTimesInConfigurationFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""DevDiv"">
                       <property name=""MSBuildBinPath"" value=""D:\some\folder\on\disk""/>
                       <property name=""p1"" value=""another""/>
                     </toolset>
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\folder""/>
                       <property name=""p2"" value=""anotherValue""/>
                     </toolset>
                     <toolset toolsVersion=""DEVDIV"">
                       <property name=""MSBuildBinPath"" value=""D:\some\folder\on\disk""/>
                       <property name=""p1"" value=""another""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               null,
                                                               GetStandardConfigurationReader(),
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.ConfigurationFile);
            });
        }
 
        [WindowsOnlyFact]
        public void CannotSetReservedPropertyInConfigFile()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\some\folder\on\disk""/>
                       <property name=""MSBuildProjectFile"" value=""newValue""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               null,
                                                               GetStandardConfigurationReader(),
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.ConfigurationFile);
            });
        }
#endif

        [WindowsOnlyFact]
        public void CannotSetReservedPropertyInRegistry()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                // Registry Read
                RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                key1.SetValue("MSBuildBinPath", @"D:\somepath");
                key1.SetValue("MSBuildProjectFile", @"SomeRegistryValue");
 
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                               null,
#endif
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
 
        [WindowsOnlyFact]
        public void CannotSetReservedPropertyInRegistrySubToolset()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                // Registry Read
                RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                RegistryKey subKey1 = key1.CreateSubKey("Foo");
 
                subKey1.SetValue("MSBuildBinPath", @"D:\somepath");
                subKey1.SetValue("MSBuildProjectFile", @"SomeRegistryValue");
 
                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                               null,
#endif
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Registry);
            });
        }
 
#if FEATURE_SYSTEM_CONFIGURATION
        /// <summary>
        /// Properties defined in previously processed toolset definitions should
        /// not affect the evaluation of subsequent toolset definitions.
        /// </summary>
        [WindowsOnlyFact]
        public void NoInterferenceBetweenToolsetDefinitions()
        {
            string v20Dir = NativeMethodsShared.IsWindows ? @"D:\20\some\folder\on\disk" : "/20/some/folder/on/disk";
            string v35Dir = NativeMethodsShared.IsWindows ? @"D:\35\some\folder\on\disk" : "/35/some/folder/on/disk";
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""" + v20Dir + @"""/>
                       <property name=""p1"" value=""another""/>
                       <property name=""p4"" value=""fourth$(p3)Value"" />
                     </toolset>
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""" + v35Dir + @"""/>
                       <property name=""p2"" value=""some$(p1)value""/>
                       <property name=""p3"" value=""propertyValue""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           null,
                                                           GetStandardConfigurationReader(),
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
 
                                                           ToolsetDefinitionLocations.ConfigurationFile);
 
            Assert.Equal(2, values.Count);
 
            Assert.Equal(v20Dir, values["2.0"].ToolsPath);
            Assert.Equal(2, values["2.0"].Properties.Count);
            Assert.Equal(@"another", values["2.0"].Properties["p1"].EvaluatedValue);
            Assert.Equal(@"fourthValue", values["2.0"].Properties["p4"].EvaluatedValue);
 
            Assert.Equal(v35Dir, values["4.0"].ToolsPath);
            Assert.Equal(2, values["4.0"].Properties.Count);
            Assert.Equal(@"somevalue", values["4.0"].Properties["p2"].EvaluatedValue);
            Assert.Equal(@"propertyValue", values["4.0"].Properties["p3"].EvaluatedValue);
        }
#endif

        /// <summary>
        /// Properties in the configuration file may refer to a registry location by using the syntax for example
        /// "$(Registry:HKEY_LOCAL_MACHINE\Software\Vendor\Tools@TaskLocation)", where "HKEY_LOCAL_MACHINE\Software\Vendor\Tools" is the key and
        /// "TaskLocation" is the name of the value.  The name of the value and the preceding "@" may be omitted if
        /// the default value is desired.
        /// </summary>
        [WindowsOnlyFact(additionalMessage: "Access local machine registry is for Windows only.")]
        public void ConfigFileInvalidRegistryExpression1()
        {
            // No location
            ConfigFileInvalidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:)""/>");
        }
 
        [WindowsOnlyFact(additionalMessage: "Access local machine registry is for Windows only.")]
        public void ConfigFileInvalidRegistryExpression2()
        {
            // Bogus key expression
            ConfigFileInvalidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:__bogus__)""/>");
        }
 
        [WindowsOnlyFact(additionalMessage: "Access local machine registry is for Windows only.")]
        public void ConfigFileInvalidRegistryExpression3()
        {
            // No registry location just @
            ConfigFileInvalidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:@)""/>");
        }
 
        [WindowsOnlyFact]
        public void ConfigFileInvalidRegistryExpression4()
        {
            // Double @
            ConfigFileInvalidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:HKEY_CURRENT_USER\Software\Vendor\Tools@@TaskLocation)""/>");
        }
 
        [WindowsOnlyFact]
        public void ConfigFileInvalidRegistryExpression5()
        {
            // Trailing @
            ConfigFileInvalidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:HKEY_CURRENT_USER\Software\Vendor\Tools@TaskLocation@)""/>");
        }
 
        [WindowsOnlyFact]
        public void ConfigFileInvalidRegistryExpression6()
        {
            // Leading @
            ConfigFileInvalidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:@HKEY_CURRENT_USER\Software\Vendor\Tools@TaskLocation)""/>");
        }
 
        [WindowsOnlyFact(additionalMessage: "Access registry is for Windows only.")]
        public void ConfigFileInvalidRegistryExpression7()
        {
            // Bogus hive
            ConfigFileInvalidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:BOGUS_HIVE\Software\Vendor\Tools@TaskLocation)""/>");
        }
 
        [WindowsOnlyFact]
        public void ConfigFileStringEmptyRegistryExpression1()
        {
            // Regular undefined property beginning with "Registry"
            ConfigFileValidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry)""/>",
                                          String.Empty);
        }
 
        [WindowsOnlyFact]
        public void ConfigFileStringEmptyRegistryExpression2()
        {
            // Nonexistent key
            ConfigFileValidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:HKEY_CURRENT_USER\Nonexistent_Key\Software\Vendor\Tools@TaskLocation)""/>",
                                          String.Empty);
        }
 
        [WindowsOnlyFact]
        public void ConfigFileNonPropertyRegistryExpression1()
        {
            // Property not terminated with paren, does not look like property
            ConfigFileValidRegistryExpressionHelper(@"<property name=""p"" value=""$(Registry:HKEY_CURRENT_USER\Software\Vendor\Tools@TaskLocation""/>",
                                          @"$(Registry:HKEY_CURRENT_USER\Software\Vendor\Tools@TaskLocation");
        }
 
        [WindowsOnlyFact]
        public void ConfigFileNonPropertyRegistryExpression2()
        {
            // Missing colon, looks like regular property (but with invalid property name chars, we will return blank as a result)
            ConfigFileValidRegistryExpressionHelper(@"<property name=""p"" value=""$(RegistryHKEY_CURRENT_USER\Software\Vendor\Tools@@TaskLocation)""/>",
                                          String.Empty);
        }
 
        [WindowsOnlyFact]
        public void ConfigFileItemExpressionsDoNotExpandInConfigurationProperties()
        {
            // Expect that item expressions such as '@(SomeItem)' are not evaluated in any way, e.g., they are treated literally
            ConfigFileValidRegistryExpressionHelper(@"<property name=""p"" value=""@(SomeItem)""/>",
                                          @"@(SomeItem)");
        }
 
        [WindowsOnlyFact(additionalMessage: "Access local machine registry is for Windows only.")]
        public void RegistryInvalidRegistryExpression1()
        {
            // Bogus key expression
            RegistryInvalidRegistryExpressionHelper("$(Registry:__bogus__)");
        }
 
        [WindowsOnlyFact]
        public void RegistryValidRegistryExpression1()
        {
            // Regular undefined property beginning with "Registry"
            RegistryValidRegistryExpressionHelper("$(Registry)", String.Empty);
        }
 
        private void RegistryInvalidRegistryExpressionHelper(string propertyExpression)
        {
            bool caught = false;
            try
            {
                // this should throw...
                RegistryValidRegistryExpressionHelper(propertyExpression, String.Empty);
            }
            catch (InvalidToolsetDefinitionException ex)
            {
                Console.WriteLine(ex.Message);
                caught = true;
            }
 
            Assert.True(caught);
        }
 
        private void RegistryValidRegistryExpressionHelper(string propertyExpression, string expectedValue)
        {
            // Registry Read
            _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "2.0");
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", "xxxx");
            key1.SetValue("p", propertyExpression);
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                           values,
                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                           GetStandardConfigurationReader(),
#endif
                                           collection.EnvironmentProperties,
                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                           ToolsetDefinitionLocations.Registry);
 
            Assert.Single(values);
            Assert.Equal(expectedValue, values["2.0"].Properties["p"].EvaluatedValue);
        }
 
        /// <summary>
        /// Tests that an invalid registry property expression causes an exception (resulting in a project load error)
        /// </summary>
        /// <param name="propertyExpression"></param>
        private void ConfigFileInvalidRegistryExpressionHelper(string propertyExpression)
        {
            bool caught = false;
            try
            {
                // this should throw...
                ConfigFileValidRegistryExpressionHelper(propertyExpression, String.Empty);
            }
            catch (InvalidToolsetDefinitionException ex)
            {
                Console.WriteLine(ex.Message);
                caught = true;
            }
 
            Assert.True(caught);
        }
 
        /// <summary>
        /// Tests that a specified registry property expression evaluates to specified value
        /// </summary>
        /// <param name="propertyExpression"></param>
        /// <param name="expectedValue"></param>
        private void ConfigFileValidRegistryExpressionHelper(string propertyExpression, string expectedValue)
        {
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""xxxx""/>
                       " + propertyExpression + @"
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                           values,
                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                           GetStandardConfigurationReader(),
#endif
                                           collection.EnvironmentProperties,
                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                           ToolsetDefinitionLocations.ConfigurationFile);
 
            Assert.Single(values);
            Assert.Equal(expectedValue, values["2.0"].Properties["p"].EvaluatedValue);
        }
 
        /// <summary>
        /// Tests the case where application configuration file overrides a value already specified in the registry
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_ConflictingPropertyValuesSameCase()
        {
            string binPath = NativeMethodsShared.IsWindows ? @"D:\somepath" : "/somepath";
            string overrideBinPath = NativeMethodsShared.IsWindows ? @"D:\somedifferentpath" : "/somedifferentpath";
            // Registry Read
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", binPath);
 
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""" + overrideBinPath + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Single(values);
            Assert.Empty(values["2.0"].Properties);
            Assert.Equal(overrideBinPath, values["2.0"].ToolsPath);
        }
 
        /// <summary>
        /// Tests the case where application configuration file overrides a value already specified in the registry,
        /// where that registry value is bogus and would otherwise throw.  However, since the config file also
        /// contains an entry for that toolset, the registry toolset never gets read, and thus never throws.
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_ConflictingPropertyValuesRegistryThrows()
        {
            string binPath = NativeMethodsShared.IsWindows ? @"D:\somepath" : "/somepath";
            // Registry Read
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", @"$([MSBuild]::SomeNonexistentPropertyFunction())");
 
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""" + binPath + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Single(values);
            Assert.Empty(values["2.0"].Properties);
            Assert.Equal(binPath, values["2.0"].ToolsPath);
        }
 
        /// <summary>
        /// Tests when properties are defined in the registry as
        /// well as in the config file for the same tools version.
        /// We should not merge them; we should take the config file ones only
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_NoMerging()
        {
            string binPath = NativeMethodsShared.IsWindows ? @"D:\somepath" : "/somepath";
            string overrideBinPath = NativeMethodsShared.IsWindows ? @"D:\someotherpath" : "/someotherpath";
            // Registry Read
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", binPath);
            key1.SetValue("SomeRegistryProperty", @"SomeRegistryValue");
 
            // Set the config file contents as needed
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""" + overrideBinPath + @"""/>
                       <property name=""SomeConfigProperty"" value=""SomeConfigValue""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Single(values);
            Assert.Single(values["2.0"].Properties);
            Assert.Equal(overrideBinPath, values["2.0"].ToolsPath);
            Assert.Null(values["2.0"].Properties["SomeRegistryProperty"]); // Was zapped
            Assert.Equal(@"SomeConfigValue", values["2.0"].Properties["SomeConfigProperty"].EvaluatedValue);
        }
 
        /// <summary>
        /// The absence of the ToolsVersion attribute on the main Project element in a project file means
        /// that the engine's default tools version should be used.
        /// </summary>
        [WindowsOnlyFact]
        public void ToolsVersionAttributeNotSpecifiedOnProjectElementAndDefaultVersionSpecifiedInRegistry()
        {
            string oldValue = Environment.GetEnvironmentVariable("MSBUILDLEGACYDEFAULTTOOLSVERSION");
 
            try
            {
                // In the new world of figuring out the ToolsVersion to use, we completely ignore the default
                // ToolsVersion in the ProjectCollection.  However, this test explicitly depends on modifying
                // that, so we need to turn the new defaulting behavior off in order to verify that this still works.
                Environment.SetEnvironmentVariable("MSBUILDLEGACYDEFAULTTOOLSVERSION", "1");
                InternalUtilities.RefreshInternalEnvironmentValues();
 
                using ProjectCollection projectCollection = new ProjectCollection();
 
                string msbuildOverrideTasksPath = null;
                projectCollection.AddToolset(new Toolset("2.0", "20toolsPath", projectCollection, msbuildOverrideTasksPath));
                projectCollection.AddToolset(new Toolset(ObjectModelHelpers.MSBuildDefaultToolsVersion, "120toolsPath", projectCollection, msbuildOverrideTasksPath));
 
                string projectPath = ObjectModelHelpers.CreateFileInTempProjectDirectory("x.proj", @"<Project />");
 
                Project project = projectCollection.LoadProject(projectPath);
 
                string defaultExpected = "Current";
                if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null)
                {
                    defaultExpected = ObjectModelHelpers.MSBuildDefaultToolsVersion;
                }
 
                Assert.Equal(defaultExpected, project.ToolsVersion);
                Assert.Equal(defaultExpected, projectCollection.DefaultToolsVersion);
            }
            finally
            {
                Environment.SetEnvironmentVariable("MSBUILDLEGACYDEFAULTTOOLSVERSION", oldValue);
                InternalUtilities.RefreshInternalEnvironmentValues();
            }
        }
 
        /// <summary>
        /// Tests the case when no values are specified in the registry
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_RegistryNotPresent()
        {
            string binPath = NativeMethodsShared.IsWindows ? @"D:\somedifferentpath" : "/somedifferentpath";
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBUILDBINPATH"" value=""" + binPath + @"""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Single(values);
            Assert.Empty(values["2.0"].Properties);
            Assert.Equal(binPath, values["2.0"].ToolsPath);
        }
 
        /// <summary>
        /// Test the case where nothing is specified in the config file
        /// Note that config file not present is same as config file
        /// with no MSBuildToolsets Section
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_ConfigFileNotPresent()
        {
            // Registry Read
            string binPath = NativeMethodsShared.IsWindows ? @"D:\somepath" : "/somepath";
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", binPath);
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Single(values);
            Assert.Empty(values["2.0"].Properties);
            Assert.Equal(binPath, values["2.0"].ToolsPath);
        }
 
        /// <summary>
        /// Tests the case where nothing is specified in registry and config file
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_RegistryAndConfigNotPresent()
        {
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            // Should either be the last-ditch 2.0 toolset, or if 2.0 is not installed, then the last-last-ditch of 4.0
            Assert.Single(values);
            if (FrameworkLocationHelper.PathToDotNetFrameworkV20 != null)
            {
                Assert.Equal("2.0", defaultToolsVersion);
            }
            else
            {
                Assert.Equal(ObjectModelHelpers.MSBuildDefaultToolsVersion, defaultToolsVersion);
            }
        }
 
        /// <summary>
        /// Tests the case when reading config file throws an exception
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_ReadConfigThrowsException()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                // Registry Read
                RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
                key1.SetValue("MSBuildBinPath", @"D:\somepath");
 
                // Set the config helper to throw exception
#if FEATURE_SYSTEM_CONFIGURATION
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"", new ConfigurationErrorsException());
#endif

                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                ToolsetReader.ReadAllToolsets(
                               values,
                               GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                               GetStandardConfigurationReader(),
#endif
                               new ProjectCollection().EnvironmentProperties,
                               new PropertyDictionary<ProjectPropertyInstance>(),
                               ToolsetDefinitionLocations.Default);
            });
        }
        /// <summary>
        /// Tests the case where reading from registry throws exception
        /// </summary>
        [WindowsOnlyFact]
        public void GetToolsetData_ReadRegistryOpenSubKeyThrowsException()
        {
            Assert.Throws<InvalidToolsetDefinitionException>(() =>
            {
                RegistryKeyWrapper mockRegistryKey =
                    new MockRegistryKey(testRegistryPath, MockRegistryKey.WhereToThrow.OpenSubKey);
 
#if FEATURE_SYSTEM_CONFIGURATION
                ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""2.0"">
                     <toolset toolsVersion=""2.0"">
                       <property name=""MSBuildBinPath"" value=""D:\somedifferentpath""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

                Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
                ToolsetReader.ReadAllToolsets(
                                                               values,
                                                               new ToolsetRegistryReader(new ProjectCollection().EnvironmentProperties, new PropertyDictionary<ProjectPropertyInstance>(), mockRegistryKey),
#if FEATURE_SYSTEM_CONFIGURATION
                                                               GetStandardConfigurationReader(),
#endif
                                                               new ProjectCollection().EnvironmentProperties,
                                                               new PropertyDictionary<ProjectPropertyInstance>(),
                                                               ToolsetDefinitionLocations.Default);
            });
        }
 
        #endregion

        #region "SetDefaultToolsetVersion tests"

        /// <summary>
        /// Tests that the default ToolsVersion is correctly resolved when specified
        /// in registry and config file
        /// </summary>
        [WindowsOnlyFact]
        public void SetDefaultToolsetVersion_SpecifiedInRegistryAndConfigFile()
        {
            // Set up registry with two tools versions and one property each
            _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "2.0");
            RegistryKey key1 = _toolsVersionsRegistryKey.CreateSubKey("2.0");
            key1.SetValue("MSBuildBinPath", @"D:\somepath");
            RegistryKey key2 = _toolsVersionsRegistryKey.CreateSubKey("4.0");
            key2.SetValue("MSBuildBinPath", @"D:\somepath2");
 
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""5.0"">
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Equal("5.0", defaultToolsVersion);
        }
 
        /// <summary>
        /// Tests that the default ToolsVersion is correctly resolved when specified in registry only
        /// </summary>
        [WindowsOnlyFact]
        public void SetDefaultToolsetVersion_SpecifiedOnlyInRegistry()
        {
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""3.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            // Set up registry with two tools versions and one property each
            _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "4.0");
            RegistryKey key2 = _toolsVersionsRegistryKey.CreateSubKey("4.0");
            key2.SetValue("MSBuildBinPath", @"D:\somepath2");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Equal("4.0", defaultToolsVersion);
        }
 
        /// <summary>
        /// Tests that the override task path is correctly resolved when specified in registry only
        /// </summary>
        [WindowsOnlyFact]
        public void SetOverrideTasks_SpecifiedOnlyInRegistry()
        {
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""3.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            string overridePath = NativeMethodsShared.IsWindows ? "c:\\TaskOverridePath" : "/TaskOverridePath";
            // Set up registry with two tools versions and one property each
            _currentVersionRegistryKey.SetValue("DefaultToolsVersion", "4.0");
            _currentVersionRegistryKey.SetValue("msbuildOverrideTasksPath", overridePath);
            RegistryKey key2 = _toolsVersionsRegistryKey.CreateSubKey("4.0");
            key2.SetValue("MSBuildBinPath", @"D:\somepath2");
            key2.SetValue("MSBuildBinPath", @"D:\OtherTaskOverridePath");
            RegistryKey key3 = _toolsVersionsRegistryKey.CreateSubKey("5.0");
            key3.SetValue("MSBuildBinPath", @"D:\somepath3");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Equal("4.0", defaultToolsVersion);
            Assert.Equal(overridePath, values["4.0"].OverrideTasksPath);
            // Assert.Equal("c:\\OtherTaskOverridePath", values["5.0"].OverrideTasksPath); // UNDONE: Per-toolset override paths don't work.
        }
 
        /// <summary>
        /// Tests that the override default toolsversion is correctly resolved when specified in registry only
        /// </summary>
        [WindowsOnlyFact]
        public void SetDefaultOverrideToolsVersion_SpecifiedOnlyInRegistry()
        {
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""3.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            // Set up registry with two tools versions and one property each
            _currentVersionRegistryKey.SetValue("DefaultOverrideToolsVersion", "13.0");
            RegistryKey key = _toolsVersionsRegistryKey.CreateSubKey("13.0");
            key.SetValue("MSBuildBinPath", @"D:\somepath2");
            RegistryKey key2 = _toolsVersionsRegistryKey.CreateSubKey("4.0");
            key2.SetValue("MSBuildBinPath", @"D:\somepath3");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Equal("13.0", values["4.0"].DefaultOverrideToolsVersion);
        }
 
#if FEATURE_SYSTEM_CONFIGURATION
        /// <summary>
        /// Tests that the default ToolsVersion is correctly resolved
        /// when specified in config file only
        /// </summary>
        [WindowsOnlyFact]
        public void SetDefaultToolsetVersion_SpecifiedOnlyInConfigFile()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""5.0"">
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
 
            Assert.Equal("5.0", defaultToolsVersion);
        }
 
        /// <summary>
        /// Tests that the override tasks path ToolsVersion is correctly resolved
        /// when specified in config file only.
        /// Also, that MSBuildOverrideTasksPath can be overridden.
        /// </summary>
        [WindowsOnlyFact]
        public void SetOverrideTaskPath_SpecifiedOnlyInConfigFile()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""5.0"" msbuildOverrideTasksPath=""C:\TaskOverride"">
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                       <property name=""MSBuildOverrideTasksPath"" value=""c:\OtherTaskOverride""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
 
            Assert.Equal("5.0", defaultToolsVersion);
            Assert.Equal("C:\\TaskOverride", values["4.0"].OverrideTasksPath);
            // Assert.Equal("C:\\OtherTaskOverride", values["5.0"].OverrideTasksPath); // UNDONE: Per-toolset override paths aren't working
        }
 
        /// <summary>
        /// Tests that the override default ToolsVersion is correctly resolved
        /// when specified in config file only.
        /// </summary>
        [WindowsOnlyFact]
        public void SetDefaultOverrideToolsVersion_SpecifiedOnlyInConfigFile()
        {
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets default=""5.0"" DefaultOverrideToolsVersion=""3.0"">
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Equal("5.0", defaultToolsVersion);
            Assert.Equal("3.0", values["4.0"].DefaultOverrideToolsVersion);
        }
#endif

        /// <summary>
        /// Tests that the default ToolsVersion is correctly resolved when specified nowhere
        /// </summary>
        [WindowsOnlyFact]
        public void SetDefaultToolsetVersion_SpecifiedNowhere()
        {
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                     </toolset>
                     <toolset toolsVersion=""5.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v4.0.x86ret\""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            string defaultToolsVersion = ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           new PropertyDictionary<ProjectPropertyInstance>(),
                                                           ToolsetDefinitionLocations.Default);
 
            string expectedDefault = "2.0";
            if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null)
            {
                expectedDefault = ObjectModelHelpers.MSBuildDefaultToolsVersion;
            }
 
            Assert.Equal(expectedDefault, defaultToolsVersion); // built-in default
            Assert.Null(values[expectedDefault].OverrideTasksPath);
            Assert.Null(values[expectedDefault].DefaultOverrideToolsVersion);
        }
 
#if FEATURE_SYSTEM_CONFIGURATION
        /// <summary>
        /// Tests that properties are properly expanded when reading them from the config file
        /// </summary>
        [WindowsOnlyFact]
        public void PropertiesInToolsetsFromConfigFileAreExpanded()
        {
            // $(COMPUTERNAME) is just a convenient env var. $(NUMBER_OF_PROCESSORS) isn't defined on Longhorn
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                       <property name=""p1"" value=""v1$(p2)""/>
                       <property name=""p2"" value=""__$(p1)__""/>
                       <property name=""p3"" value=""$(COMPUTERNAME)""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           collection.GlobalPropertiesCollection,
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Equal("v1", values["4.0"].Properties["p1"].EvaluatedValue);
            // Properties can refer to other properties also defined in the config file
            Assert.Equal("__v1__", values["4.0"].Properties["p2"].EvaluatedValue);
            Assert.Equal(Environment.MachineName, values["4.0"].Properties["p3"].EvaluatedValue);
        }
 
        /// <summary>
        /// Tests that properties in MSBuildToolsPath are properly expanded when reading them from the config file
        /// </summary>
        [WindowsOnlyFact]
        public void PropertiesInToolsetsFromConfigFileAreExpandedInToolsPath()
        {
            string binPathConfig = NativeMethodsShared.IsWindows ?
                @"D:\windows\$(p1)\Framework\v2.0.x86ret\$(COMPUTERNAME)" :
                "/windows/$(p1)/Framework/v2.0.x86ret";
            string toolsPathConfig = NativeMethodsShared.IsWindows ?
                @"D:\$(p2)\$(p1)\Framework\v2.0.x86ret\$(COMPUTERNAME)" :
                "/$(p2)/$(p1)/Framework/v2.0.x86ret";
 
            // $(COMPUTERNAME) is just a convenient env var. $(NUMBER_OF_PROCESSORS) isn't defined on Longhorn
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""4.0"">
                       <property name=""p1"" value=""Microsoft.NET""/>
                       <property name=""p2"" value=""windows""/>
                       <property name=""MSBuildBinPath"" value=""" + binPathConfig + @"""/>
                       <property name=""MSBuildToolsPath"" value=""" + toolsPathConfig + @"""/>
                       <property name=""p3"" value=""v3$(MSBuildToolsPath)""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
 
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            using var collection = new ProjectCollection();
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           collection.EnvironmentProperties,
                                                           collection.GlobalPropertiesCollection,
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Equal("Microsoft.NET", values["4.0"].Properties["p1"].EvaluatedValue);
            Assert.Equal("windows", values["4.0"].Properties["p2"].EvaluatedValue);
            string expectedToolsPath = NativeMethodsShared.IsWindows
                                           ? @"D:\windows\Microsoft.NET\Framework\v2.0.x86ret\"
                                             + Environment.MachineName
                                           : "/windows/Microsoft.NET/Framework/v2.0.x86ret";
            Assert.Equal(expectedToolsPath, values["4.0"].ToolsPath);
            Assert.Equal("v3" + expectedToolsPath, values["4.0"].Properties["p3"].EvaluatedValue);
        }
#endif

        /// <summary>
        /// Global properties are available, but they cannot be overwritten by other toolset properties, just as they cannot
        /// be overwritten by project file properties.
        /// </summary>
        [WindowsOnlyFact]
        public void GlobalPropertiesInToolsetsAreExpandedButAreNotOverwritten()
        {
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReaderTestHelper.WriteConfigFile(@"
                 <configuration>
                   <configSections>
                     <section name=""msbuildToolsets"" type=""Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build"" />
                   </configSections>
                   <msbuildToolsets>
                     <toolset toolsVersion=""4.0"">
                       <property name=""MSBuildBinPath"" value=""D:\windows\Microsoft.NET\Framework\v2.0.x86ret\""/>
                       <property name=""p1"" value=""$(gp1)""/>
                       <property name=""gp1"" value=""v2""/>
                       <property name=""p2"" value=""$(gp1)""/>
                     </toolset>
                   </msbuildToolsets>
                 </configuration>");
#endif

            Dictionary<string, string> globalProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            globalProperties["gp1"] = "gv1";
            using ProjectCollection e = new ProjectCollection(globalProperties, null, ToolsetDefinitionLocations.None);
            Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase);
 
            ToolsetReader.ReadAllToolsets(
                                                           values,
                                                           GetStandardRegistryReader(),
#if FEATURE_SYSTEM_CONFIGURATION
                                                           GetStandardConfigurationReader(),
#endif
                                                           e.EnvironmentProperties,
                                                           e.GlobalPropertiesCollection,
                                                           ToolsetDefinitionLocations.Default);
 
            Assert.Equal("gv1", values["4.0"].Properties["p1"].EvaluatedValue);
            Assert.Equal("gv1", values["4.0"].Properties["p2"].EvaluatedValue);
        }
 
        #endregion

        private ToolsetRegistryReader GetStandardRegistryReader()
        {
            using var collection = new ProjectCollection();
#pragma warning disable CA2000 // The return object depends on the registry key that should not be disposed in this scope.
            var registryKey = new MockRegistryKey(testRegistryPath);
#pragma warning restore CA2000 // The return object depends on the registry key that should not be disposed in this scope.
            return new ToolsetRegistryReader(collection.EnvironmentProperties, new PropertyDictionary<ProjectPropertyInstance>(), registryKey);
        }
 
#if FEATURE_SYSTEM_CONFIGURATION
        private ToolsetConfigurationReader GetStandardConfigurationReader()
        {
            using var collection = new ProjectCollection();
            return new ToolsetConfigurationReader(collection.EnvironmentProperties, new PropertyDictionary<ProjectPropertyInstance>(), ToolsetConfigurationReaderTestHelper.ReadApplicationConfigurationTest);
        }
#endif
    }
 
#endif
 
#if FEATURE_WIN32_REGISTRY
    internal sealed class MockRegistryKey : RegistryKeyWrapper
    {
        public enum WhereToThrow
        {
            None,
            Name,
            GetValue,
            GetValueNames,
            GetSubKeyNames,
            OpenSubKey
        }
 
        private WhereToThrow _whereToThrow = WhereToThrow.None;
        private string _subKeyThatDoesNotExist = null;
 
        /// <summary>
        /// Construct the mock key with a specified key
        /// </summary>
        /// <param name="path"></param>
        private MockRegistryKey(RegistryKey wrappedKey, RegistryKey registryHive)
            : base(wrappedKey, registryHive)
        { }
 
        /// <summary>
        /// Construct the mock key with a wrapper
        /// </summary>
        /// <param name="path"></param>
        public MockRegistryKey(string path)
            : base(path, Registry.CurrentUser)
        { }
 
        /// <summary>
        /// Construct the mock key with a wrapper and a designated method
        /// to throw from
        /// </summary>
        /// <param name="path"></param>
        /// <param name="whereToThrow"></param>
        public MockRegistryKey(string path, WhereToThrow whereToThrow)
            : base(path, Registry.CurrentUser)
        {
            _whereToThrow = whereToThrow;
        }
 
        /// <summary>
        /// Construct the mock key with a wrapper and a designated subkey
        /// to refuse to open
        /// </summary>
        /// <param name="path"></param>
        /// <param name="whereToThrow"></param>
        public MockRegistryKey(string path, string subKeyThatDoesNotExist)
            : base(path, Registry.CurrentUser)
        {
            _subKeyThatDoesNotExist = subKeyThatDoesNotExist;
        }
 
        /// <summary>
        /// Name of the registry key
        /// </summary>
        public override string Name
        {
            get
            {
                if (_whereToThrow == WhereToThrow.Name)
                {
                    throw new RegistryException("registryException", "registry");
                }
                return base.Name;
            }
        }
 
        /// <summary>
        /// Gets the value with name "name" stored under this registry key
        /// </summary>
        public override object GetValue(string name)
        {
            if (_whereToThrow == WhereToThrow.GetValue)
            {
                throw new RegistryException("registryException", "registry");
            }
            return base.GetValue(name);
        }
 
        /// <summary>
        /// Gets the names of all values underneath this registry key
        /// </summary>
        public override string[] GetValueNames()
        {
            if (_whereToThrow == WhereToThrow.GetValueNames)
            {
                throw new RegistryException("registryException", "registry");
            }
            return base.GetValueNames();
        }
 
        /// <summary>
        /// Gets the names of all sub keys immediately below this registry key
        /// </summary>
        /// <returns></returns>
        public override string[] GetSubKeyNames()
        {
            if (_whereToThrow == WhereToThrow.GetSubKeyNames)
            {
                throw new RegistryException("registryException", "registry");
            }
            return base.GetSubKeyNames();
        }
 
        /// <summary>
        /// Returns the sub key with name "name" as a read only key
        /// </summary>
        public override RegistryKeyWrapper OpenSubKey(string name)
        {
            if (_whereToThrow == WhereToThrow.OpenSubKey)
            {
                throw new RegistryException("registryException", "registry");
            }
 
            if (_subKeyThatDoesNotExist == name)
            {
                // Return wrapper around null key
                return new MockRegistryKey((RegistryKey)null, Registry.LocalMachine);
            }
 
            return base.OpenSubKey(name);
        }
    }
#endif
}