File: EscapingInProjects_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.IO;
using System.Linq;
#if FEATURE_COMPILE_IN_TESTS
using System.Reflection;
#endif
 
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
 
#if FEATURE_COMPILE_IN_TESTS
using EscapingUtilities = Microsoft.Build.Shared.EscapingUtilities;
#endif
using FileUtilities = Microsoft.Build.Shared.FileUtilities;
using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException;
using ResourceUtilities = Microsoft.Build.Shared.ResourceUtilities;
using Shouldly;
using Xunit;
using Xunit.Abstractions;
#if FEATURE_COMPILE_IN_TESTS
using Microsoft.Build.Shared;
#endif
 
#nullable disable
 
namespace Microsoft.Build.UnitTests.EscapingInProjects_Tests
{
    /// <summary>
    /// Test task that just logs the parameters it receives.
    /// </summary>
    public class MyTestTask : Task
    {
        private ITaskItem _taskItemParam;
        public ITaskItem TaskItemParam
        {
            get
            {
                return _taskItemParam;
            }
 
            set
            {
                _taskItemParam = value;
            }
        }
 
        public override bool Execute()
        {
            if (TaskItemParam != null)
            {
                Log.LogMessageFromText("Received TaskItemParam: " + TaskItemParam.ItemSpec, MessageImportance.High);
            }
 
            return true;
        }
    }
 
    public class SimpleScenarios : IDisposable
    {
        private readonly ITestOutputHelper _output;
 
        public SimpleScenarios(ITestOutputHelper testOutputHelper)
        {
            _output = testOutputHelper;
        }
 
        /// <summary>
        /// Since we create a project with the same name in many of these tests, and two projects with
        /// the same name cannot be loaded in a ProjectCollection at the same time, we should unload the
        /// GlobalProjectCollection (into which all of these projects are placed by default) after each test.
        /// </summary>
        public void Dispose()
        {
            ProjectCollection.GlobalProjectCollection.UnloadAllProjects();
        }
 
        /// <summary>
        /// Make sure I can define a property with escaped characters and pass it into
        /// a string parameter of a task, in this case the Message task.
        /// </summary>
        [Fact]
        public void SemicolonInPropertyPassedIntoStringParam()
        {
            MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(@"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <PropertyGroup>
                        <MyPropertyWithSemicolons>abc %3b def %3b ghi</MyPropertyWithSemicolons>
                    </PropertyGroup>
                    <Target Name=`Build`>
                        <Message Text=`Property value is '$(MyPropertyWithSemicolons)'` />
                    </Target>
                </Project>
                ", logger: new MockLogger(_output));
 
            logger.AssertLogContains("Property value is 'abc ; def ; ghi'");
        }
 
        /// <summary>
        /// Make sure I can define a property with escaped characters and pass it into
        /// a string parameter of a task, in this case the Message task.
        /// </summary>
        [Fact]
        public void SemicolonInPropertyPassedIntoStringParam_UsingTaskHost()
        {
            MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(@"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <UsingTask TaskName=`Message` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` TaskFactory=`TaskHostFactory` />
                    <PropertyGroup>
                        <MyPropertyWithSemicolons>abc %3b def %3b ghi</MyPropertyWithSemicolons>
                    </PropertyGroup>
                    <Target Name=`Build`>
                        <Message Text=`Property value is '$(MyPropertyWithSemicolons)'` />
                    </Target>
                </Project>
                ", logger: new MockLogger(_output));
 
            logger.AssertLogContains("Property value is 'abc ; def ; ghi'");
        }
 
#if FEATURE_ASSEMBLY_LOCATION
        /// <summary>
        /// Make sure I can define a property with escaped characters and pass it into
        /// an ITaskItem[] task parameter.
        /// </summary>
        [Fact]
        public void SemicolonInPropertyPassedIntoITaskItemParam()
        {
            MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(@$"
 
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <UsingTask TaskName=`Microsoft.Build.UnitTests.EscapingInProjects_Tests.MyTestTask` AssemblyFile=`{new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath}` />
 
                    <PropertyGroup>
                        <MyPropertyWithSemicolons>abc %3b def %3b ghi</MyPropertyWithSemicolons>
                    </PropertyGroup>
 
                    <Target Name=`Build`>
                        <MyTestTask TaskItemParam=`123 $(MyPropertyWithSemicolons) 789` />
                    </Target>
 
                </Project>
 
                ",
                logger: new MockLogger(_output));
 
            logger.AssertLogContains("Received TaskItemParam: 123 abc ; def ; ghi 789");
        }
 
        /// <summary>
        /// Make sure I can define a property with escaped characters and pass it into
        /// an ITaskItem[] task parameter.
        /// </summary>
        [Fact]
        public void SemicolonInPropertyPassedIntoITaskItemParam_UsingTaskHost()
        {
            MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(String.Format(@"
 
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <UsingTask TaskName=`Microsoft.Build.UnitTests.EscapingInProjects_Tests.MyTestTask` AssemblyFile=`{0}` TaskFactory=`TaskHostFactory` />
 
                    <PropertyGroup>
                        <MyPropertyWithSemicolons>abc %3b def %3b ghi</MyPropertyWithSemicolons>
                    </PropertyGroup>
 
                    <Target Name=`Build`>
                        <MyTestTask TaskItemParam=`123 $(MyPropertyWithSemicolons) 789` />
                    </Target>
 
                </Project>
 
                ", new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath),
                logger: new MockLogger(_output));
 
            logger.AssertLogContains("Received TaskItemParam: 123 abc ; def ; ghi 789");
        }
#endif
 
        /// <summary>
        /// If I try to add a new item to a project, and my new item's Include has an unescaped semicolon
        /// in it, then we shouldn't try to match it up against any existing wildcards.  This is a really
        /// bizarre scenario ... the caller probably meant to escape the semicolon.
        /// </summary>
        [Fact]
        public void AddNewItemWithSemicolon()
        {
            // ************************************
            //               BEFORE
            // ************************************
            string projectOriginalContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <ItemGroup>
                        <MyWildCard Include=`*.weirdo`/>
                    </ItemGroup>
                </Project>
                ";
 
 
            // ************************************
            //               AFTER
            // ************************************
            string projectNewExpectedContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <ItemGroup>
                        <MyWildCard Include=`*.weirdo`/>
                        <MyWildCard Include=`foo;bar.weirdo`/>
                    </ItemGroup>
                </Project>
                ";
 
            Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
            project.AddItem("MyWildCard", "foo;bar.weirdo");
 
            Helpers.CompareProjectXml(projectNewExpectedContents, project.Xml.RawXml);
        }
 
        /// <summary>
        /// If I try to add a new item to a project, and my new item's Include has a property that
        /// contains an unescaped semicolon in it, then we shouldn't try to match it up against any existing
        /// wildcards.
        /// </summary>
        [Fact]
        public void AddNewItemWithPropertyContainingSemicolon()
        {
            // ************************************
            //               BEFORE
            // ************************************
            string projectOriginalContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <PropertyGroup>
                        <FilenameWithSemicolon>foo;bar</FilenameWithSemicolon>
                    </PropertyGroup>
                    <ItemGroup>
                        <MyWildCard Include=`*.weirdo`/>
                    </ItemGroup>
                </Project>
                ";
 
 
            // ************************************
            //               AFTER
            // ************************************
            string projectNewExpectedContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <PropertyGroup>
                        <FilenameWithSemicolon>foo;bar</FilenameWithSemicolon>
                    </PropertyGroup>
                    <ItemGroup>
                        <MyWildCard Include=`$(FilenameWithSemicolon).weirdo`/>
                        <MyWildCard Include=`*.weirdo`/>
                    </ItemGroup>
                </Project>
                ";
 
 
            Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
            project.AddItem("MyWildCard", "$(FilenameWithSemicolon).weirdo");
 
            Helpers.CompareProjectXml(projectNewExpectedContents, project.Xml.RawXml);
        }
 
        /// <summary>
        /// If I try to modify an item in a project, and my new item's Include has an unescaped semicolon
        /// in it, then we shouldn't try to match it up against any existing wildcards.  This is a really
        /// bizarre scenario ... the caller probably meant to escape the semicolon.
        /// </summary>
        [Fact]
        public void ModifyItemIncludeSemicolon()
        {
            // ************************************
            //               BEFORE
            // ************************************
            string projectOriginalContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <ItemGroup>
                        <MyWildcard Include=`*.weirdo` />
                    </ItemGroup>
 
                </Project>
                ";
 
 
            // ************************************
            //               AFTER
            // ************************************
            string projectNewExpectedContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <ItemGroup>
                        <MyWildcard Include=`a.weirdo` />
                        <MyWildcard Include=`foo;bar.weirdo` />
                        <MyWildcard Include=`c.weirdo` />
                    </ItemGroup>
 
                </Project>
                ";
 
            try
            {
                // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo.
                EscapingInProjectsHelper.CreateThreeWeirdoFiles();
 
                Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
 
                EscapingInProjectsHelper.ModifyItemOfTypeInProject(project, "MyWildcard", "b.weirdo", "foo;bar.weirdo");
 
                Helpers.CompareProjectXml(projectNewExpectedContents, project.Xml.RawXml);
            }
            finally
            {
                ObjectModelHelpers.DeleteTempProjectDirectory();
            }
        }
 
        /// <summary>
        /// If I try to modify an item in a project, and my new item's Include has an escaped semicolon
        /// in it, and it matches the existing wildcard, then we shouldn't need to modify the project file.
        /// </summary>
        [Fact]
        public void ModifyItemIncludeEscapedSemicolon()
        {
            // ************************************
            //               BEFORE
            // ************************************
            string projectOriginalContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <ItemGroup>
                        <MyWildcard Include=`*.weirdo` />
                    </ItemGroup>
 
                </Project>
                ";
 
 
            // ************************************
            //               AFTER
            // ************************************
            string projectNewExpectedContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <ItemGroup>
                        <MyWildcard Include=`*.weirdo` />
                    </ItemGroup>
 
                </Project>
                ";
 
            try
            {
                // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo.
                EscapingInProjectsHelper.CreateThreeWeirdoFiles();
 
                Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
 
                IEnumerable<ProjectItem> newItems = EscapingInProjectsHelper.ModifyItemOfTypeInProject(project, "MyWildcard", "b.weirdo", "foo%253Bbar.weirdo");
 
                Assert.Single(newItems);
                Assert.Equal("*.weirdo", newItems.First().UnevaluatedInclude);
                Assert.Equal("foo%3Bbar.weirdo", newItems.First().EvaluatedInclude);
                Assert.Equal("foo%253Bbar.weirdo", Project.GetEvaluatedItemIncludeEscaped(newItems.First()));
 
                Helpers.CompareProjectXml(projectNewExpectedContents, project.Xml.RawXml);
            }
            finally
            {
                ObjectModelHelpers.DeleteTempProjectDirectory();
            }
        }
 
        /// <summary>
        /// If I try to modify an item in a project, and my new item's Include has a property that
        /// contains an unescaped semicolon in it, then we shouldn't try to match it up against any existing
        /// wildcards.
        /// </summary>
        [Fact]
        public void ModifyItemAddPropertyContainingSemicolon()
        {
            // ************************************
            //               BEFORE
            // ************************************
            string projectOriginalContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <PropertyGroup>
                        <FilenameWithSemicolon>foo;bar</FilenameWithSemicolon>
                    </PropertyGroup>
 
                    <ItemGroup>
                        <MyWildcard Include=`*.weirdo` />
                    </ItemGroup>
 
                </Project>
                ";
 
 
            // ************************************
            //               AFTER
            // ************************************
            string projectNewExpectedContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <PropertyGroup>
                        <FilenameWithSemicolon>foo;bar</FilenameWithSemicolon>
                    </PropertyGroup>
 
                    <ItemGroup>
                        <MyWildcard Include=`a.weirdo` />
                        <MyWildcard Include=`$(FilenameWithSemicolon).weirdo` />
                        <MyWildcard Include=`c.weirdo` />
                    </ItemGroup>
 
                </Project>
                ";
 
            try
            {
                // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo.
                EscapingInProjectsHelper.CreateThreeWeirdoFiles();
 
                Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
 
                EscapingInProjectsHelper.ModifyItemOfTypeInProject(project, "MyWildcard", "b.weirdo", "$(FilenameWithSemicolon).weirdo");
 
                Helpers.CompareProjectXml(projectNewExpectedContents, project.Xml.RawXml);
            }
            finally
            {
                ObjectModelHelpers.DeleteTempProjectDirectory();
            }
        }
 
        /// <summary>
        /// Make sure that character escaping works as expected when adding a new item that matches
        /// an existing wildcarded item in the project file.
        /// </summary>
        [Fact]
        public void AddNewItemThatMatchesWildcard1()
        {
            // ************************************
            //               BEFORE
            // ************************************
            string projectOriginalContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <ItemGroup>
                        <MyWildCard Include=`*.weirdo`/>
                    </ItemGroup>
                </Project>
                ";
 
 
            // ************************************
            //               AFTER
            // ************************************
            string projectNewExpectedContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <ItemGroup>
                        <MyWildCard Include=`*.weirdo`/>
                    </ItemGroup>
                </Project>
                ";
 
            Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
            IEnumerable<ProjectItem> newItems = project.AddItem("MyWildCard", "foo%253bbar.weirdo");
 
            Helpers.CompareProjectXml(projectNewExpectedContents, project.Xml.RawXml);
 
            Assert.Single(newItems);
            Assert.Equal("MyWildCard", newItems.First().ItemType); // "Newly added item should have correct ItemType"
            Assert.Equal("*.weirdo", newItems.First().UnevaluatedInclude); // "Newly added item should have correct UnevaluatedInclude"
            Assert.Equal("foo%253bbar.weirdo", Project.GetEvaluatedItemIncludeEscaped(newItems.First())); // "Newly added item should have correct EvaluatedIncludeEscaped"
            Assert.Equal("foo%3bbar.weirdo", newItems.First().EvaluatedInclude); // "Newly added item should have correct EvaluatedInclude"
        }
 
        /// <summary>
        /// Make sure that character escaping works as expected when adding a new item that matches
        /// an existing wildcarded item in the project file.
        /// </summary>
        [Fact]
        public void AddNewItemThatMatchesWildcard2()
        {
            // ************************************
            //               BEFORE
            // ************************************
            string projectOriginalContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <ItemGroup>
                        <MyWildCard Include=`*.AAA%253bBBB`/>
                    </ItemGroup>
                </Project>
                ";
 
 
            // ************************************
            //               AFTER
            // ************************************
            string projectNewExpectedContents = @"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <ItemGroup>
                        <MyWildCard Include=`*.AAA%253bBBB`/>
                    </ItemGroup>
                </Project>
                ";
 
            Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
            IEnumerable<ProjectItem> newItems = project.AddItem("MyWildCard", "foo.AAA%253bBBB");
 
            Helpers.CompareProjectXml(projectNewExpectedContents, project.Xml.RawXml);
 
            Assert.Single(newItems);
            Assert.Equal("MyWildCard", newItems.First().ItemType); // "Newly added item should have correct ItemType"
            Assert.Equal("*.AAA%253bBBB", newItems.First().UnevaluatedInclude); // "Newly added item should have correct UnevaluatedInclude"
            Assert.Equal("foo.AAA%253bBBB", Project.GetEvaluatedItemIncludeEscaped(newItems.First())); // "Newly added item should have correct EvaluatedIncludeEscaped"
            Assert.Equal("foo.AAA%3bBBB", newItems.First().EvaluatedInclude); // "Newly added item should have correct EvaluatedInclude"
        }
 
        /// <summary>
        /// Make sure that all inferred task outputs (those that are determined without actually
        /// executing the task) are left escaped when they become real items in the engine, and
        /// they only get unescaped when fed into a subsequent task.
        /// </summary>
        [Fact]
        public void InferEscapedOutputsFromTask()
        {
            string inputFile = null;
            string outputFile = null;
 
            try
            {
                inputFile = FileUtilities.GetTemporaryFile();
                outputFile = FileUtilities.GetTemporaryFile();
 
                MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(String.Format(@"
 
                <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion`>
 
                    <Target Name=`GenerateResources` Inputs=`{0}` Outputs=`{1}`>
                        <NonExistentTask OutputResources=`aaa%253bbbb.resx; ccc%253bddd.resx`>
                            <Output ItemName=`Resource` TaskParameter=`OutputResources`/>
                        </NonExistentTask>
                    </Target>
 
                    <Target Name=`Build` DependsOnTargets=`GenerateResources`>
                        <Message Text=`Resources = @(Resource)`/>
                    </Target>
 
                </Project>
 
                ", inputFile, outputFile),
                logger: new MockLogger(_output));
 
                logger.AssertLogContains("Resources = aaa%3bbbb.resx;ccc%3bddd.resx");
            }
            finally
            {
                if (inputFile != null)
                {
                    File.Delete(inputFile);
                }
 
                if (outputFile != null)
                {
                    File.Delete(outputFile);
                }
            }
        }
 
        /// <summary>
        /// Do an item transform, where the transform expression contains an unescaped semicolon as well
        /// as an escaped percent sign.
        /// </summary>
        [Fact]
        public void ItemTransformContainingSemicolon()
        {
            MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(@"
 
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <ItemGroup>
                        <TextFile Include=`X.txt`/>
                        <TextFile Include=`Y.txt`/>
                        <TextFile Include=`Z.txt`/>
                    </ItemGroup>
                    <Target Name=`Build`>
                        <Message Text=`Transformed item list: '@(TextFile->'%(FileName);%(FileName)%253b%(FileName)%(Extension)','    ')'` />
                    </Target>
                </Project>
 
                ", logger: new MockLogger(_output));
 
            logger.AssertLogContains("Transformed item list: 'X;X%3bX.txt    Y;Y%3bY.txt    Z;Z%3bZ.txt'");
        }
 
        /// <summary>
        /// Do an item transform, where the transform expression contains an unescaped semicolon as well
        /// as an escaped percent sign.
        /// </summary>
        [Fact]
        public void ItemTransformContainingSemicolon_InTaskHost()
        {
            MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(@"
 
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <UsingTask TaskName=`Message` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` TaskFactory=`TaskHostFactory` />
 
                    <ItemGroup>
                        <TextFile Include=`X.txt`/>
                        <TextFile Include=`Y.txt`/>
                        <TextFile Include=`Z.txt`/>
                    </ItemGroup>
                    <Target Name=`Build`>
                        <Message Text=`Transformed item list: '@(TextFile->'%(FileName);%(FileName)%253b%(FileName)%(Extension)','    ')'` />
                    </Target>
                </Project>
 
                ", logger: new MockLogger(_output));
 
            logger.AssertLogContains("Transformed item list: 'X;X%3bX.txt    Y;Y%3bY.txt    Z;Z%3bZ.txt'");
        }
 
        /// <summary>
        /// Tests that when we add an item and are in a directory with characters in need of escaping, and the
        /// item's FullPath metadata is retrieved, that a properly un-escaped version of the path is returned
        /// </summary>
        [Fact]
        public void FullPathMetadataOnItemUnescaped()
        {
            string projectName = "foo.proj";
            string projectRelativePath = "(jay's parens test)";
            string path = Path.Combine(Path.GetTempPath(), projectRelativePath);
            string projectAbsolutePath = Path.Combine(path, projectName);
 
            try
            {
                Directory.CreateDirectory(path);
 
                ProjectRootElement projectElement = ProjectRootElement.Create(projectAbsolutePath);
                ProjectItemGroupElement itemgroup = projectElement.AddItemGroup();
                itemgroup.AddItem("ProjectFile", projectName);
 
                using var collection = new ProjectCollection();
                Project project = new Project(projectElement, null, null, collection);
                ProjectInstance projectInstance = project.CreateProjectInstance();
 
                IEnumerable<ProjectItemInstance> items = projectInstance.GetItems("ProjectFile");
                Assert.Equal(projectAbsolutePath, items.First().GetMetadataValue("FullPath"));
            }
            finally
            {
                if (File.Exists(projectAbsolutePath))
                {
                    File.Delete(projectAbsolutePath);
                }
 
                if (Directory.Exists(path))
                {
                    FileUtilities.DeleteWithoutTrailingBackslash(path);
                }
            }
        }
 
 
        /// <summary>
        /// Test that we can pass in global properties containing escaped characters and they
        /// won't be unescaped.
        /// </summary>
        [Fact]
        public void GlobalPropertyWithEscapedCharacters()
        {
            MockLogger logger = new MockLogger();
            Project project = ObjectModelHelpers.CreateInMemoryProject(@"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <Target Name=`Build`>
                        <Message Text=`MyGlobalProperty = '$(MyGlobalProperty)'` />
                    </Target>
                </Project>
                ");
 
            project.SetGlobalProperty("MyGlobalProperty", "foo%253bbar");
 
            bool success = project.Build(logger);
            Assert.True(success); // "Build failed.  See test output (Attachments in Azure Pipelines) for details"
 
            logger.AssertLogContains("MyGlobalProperty = 'foo%3bbar'");
        }
 
        /// <summary>
        /// If %2A (escaped '*') or %3F (escaped '?') is in an item's Include, it should be treated
        /// literally, not as a wildcard
        /// </summary>
        [Fact]
        public void EscapedWildcardsShouldNotBeExpanded()
        {
            MockLogger logger = new MockLogger();
 
            try
            {
                // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo.
                EscapingInProjectsHelper.CreateThreeWeirdoFiles();
                Project project = ObjectModelHelpers.CreateInMemoryProject(@"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <Target Name=`t`>
                        <ItemGroup>
                            <type Include=`%2A` Exclude=``/>
                        </ItemGroup>
                        <Message Text=`[@(type)]`/>
                    </Target>
                </Project>
                ");
 
                bool success = project.Build(logger);
                Assert.True(success); // "Build failed.  See test output (Attachments in Azure Pipelines) for details"
                logger.AssertLogContains("[*]");
            }
            finally
            {
                ObjectModelHelpers.DeleteTempProjectDirectory();
            }
        }
 
        /// <summary>
        /// If %2A (escaped '*') or %3F (escaped '?') is in an item's Include, it should be treated
        /// literally, not as a wildcard
        /// </summary>
        [Fact]
        public void EscapedWildcardsShouldNotBeExpanded_InTaskHost()
        {
            MockLogger logger = new();
 
            try
            {
                // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo.
                EscapingInProjectsHelper.CreateThreeWeirdoFiles();
                Project project = ObjectModelHelpers.CreateInMemoryProject(@"
                <Project>
                    <UsingTask TaskName=`Message` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` TaskFactory=`TaskHostFactory` />
 
                    <Target Name=`t`>
                        <ItemGroup>
                            <type Include=`%2A` Exclude=``/>
                        </ItemGroup>
                        <Message Text=`[@(type)]`/>
                    </Target>
                </Project>
                ");
 
                project.Build(logger).ShouldBeTrue("Build failed.  See test output (Attachments in Azure Pipelines) for details");
                logger.AssertLogContains("[*]");
            }
            finally
            {
                ObjectModelHelpers.DeleteTempProjectDirectory();
            }
        }
 
        /// <summary>
        /// Parity with Orcas: Target names are always unescaped, and in fact, if there are two targets,
        /// one the escaped version of the other, the second will override the first as though they had the
        /// same name.
        /// </summary>
        [Fact]
        public void TargetNamesAlwaysUnescaped()
        {
            bool exceptionCaught = false;
 
            try
            {
                ObjectModelHelpers.CreateInMemoryProject(@"
                <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                    <Target Name=`%24` />
                </Project>
                ");
            }
            catch (InvalidProjectFileException ex)
            {
                string expectedErrorMessage = ResourceUtilities.FormatResourceStringStripCodeAndKeyword("NameInvalid", "$", "$");
                Assert.Equal(expectedErrorMessage, ex.Message); // "Wrong error message"
                exceptionCaught = true;
            }
 
            Assert.True(exceptionCaught); // "Expected an InvalidProjectFileException"
        }
 
        /// <summary>
        /// Parity with Orcas: Target names are always unescaped, and in fact, if there are two targets,
        /// one the escaped version of the other, the second will override the first as though they had the
        /// same name.
        /// </summary>
        [Fact]
        public void TargetNamesAlwaysUnescaped_Override()
        {
            Project project = ObjectModelHelpers.CreateInMemoryProject(@"
            <Project ToolsVersion=`msbuilddefaulttoolsversion`>
                <Target Name=`%3B`>
                    <Message Text=`[WRONG]` />
                </Target>
                <Target Name=`;`>
                    <Message Text=`[OVERRIDE]` />
                </Target>
            </Project>
            ");
            MockLogger logger = new MockLogger();
 
            bool success = project.Build(logger);
            Assert.True(success); // "Build failed.  See test output (Attachments in Azure Pipelines) for details"
            logger.AssertLogContains("[OVERRIDE]");
        }
 
        /// <summary>
        /// Tests that when we set metadata through the evaluation model, we do the right thing
        /// </summary>
        [Fact]
        public void SpecialCharactersInMetadataValueConstruction()
        {
            string projectString = @"
                <Project DefaultTargets=""Build"" ToolsVersion=""msbuilddefaulttoolsversion"">
                    <ItemGroup>
                        <None Include='MetadataTests'>
                            <EscapedSemicolon>%3B</EscapedSemicolon>
                            <EscapedDollarSign>%24</EscapedDollarSign>
                        </None>
                    </ItemGroup>
                </Project>";
            using System.Xml.XmlReader reader = new System.Xml.XmlTextReader(new StringReader(projectString));
            Project project = new Project(reader);
            ProjectItem item = project.GetItems("None").Single();
 
            EscapingInProjectsHelper.SpecialCharactersInMetadataValueTests(item);
        }
 
        /// <summary>
        /// Tests that when we set metadata through the evaluation model, we do the right thing
        /// </summary>
        [Fact]
        public void SpecialCharactersInMetadataValueEvaluation()
        {
            Project project = new Project();
            ProjectItem item = project.AddItem("None", "MetadataTests", new Dictionary<string, string> {
                {"EscapedSemicolon", "%3B"}, // Microsoft.Build.Evaluation.ProjectCollection.Escape(";")
                {"EscapedDollarSign", "%24"}, // Microsoft.Build.Evaluation.ProjectCollection.Escape("$")
            }).Single();
 
            EscapingInProjectsHelper.SpecialCharactersInMetadataValueTests(item);
            project.ReevaluateIfNecessary();
            EscapingInProjectsHelper.SpecialCharactersInMetadataValueTests(item);
        }
 
        /// <summary>
        /// Say you have a scenario where a user is allowed to specify an arbitrary set of files (or
        /// any sort of items) and expects to be able to get them back out as they were sent in.  In addition,
        /// the user can specify a macro (property) that can resolve to yet another arbitrary set of items.
        /// We want to make sure that we do the right thing (assuming that the user escaped the information
        /// correctly coming in) and don't mess up their set of items
        /// </summary>
        [Fact]
        public void CanGetCorrectListOfItemsWithSemicolonsInThem()
        {
            string projectString = @"
                <Project DefaultTargets=""Build"" ToolsVersion=""msbuilddefaulttoolsversion"">
                    <PropertyGroup>
                        <MyUserMacro>foo%3bbar</MyUserMacro>
                    </PropertyGroup>
                    <ItemGroup>
                        <DifferentList Include=""a"" />
                        <DifferentList Include=""b%3bc"" />
                        <DifferentList Include=""$(MyUserMacro)"" />
                    </ItemGroup>
                </Project>";
 
            using System.Xml.XmlReader reader = new System.Xml.XmlTextReader(new StringReader(projectString));
            Project project = new Project(reader);
            IEnumerable<ProjectItem> items = project.GetItems("DifferentList");
 
            Assert.Equal(3, items.Count());
            Assert.Equal("a", items.ElementAt(0).EvaluatedInclude);
            Assert.Equal("b;c", items.ElementAt(1).EvaluatedInclude);
            Assert.Equal("foo;bar", items.ElementAt(2).EvaluatedInclude);
        }
 
        /// <summary>
        /// Say you have a scenario where a user is allowed to specify an arbitrary set of files (or
        /// any sort of items) and expects to be able to get them back out as they were sent in.  In addition,
        /// the user can specify a macro (property) that can resolve to yet another arbitrary set of items.
        /// We want to make sure that we do the right thing (assuming that the user escaped the information
        /// correctly coming in) and don't mess up their set of items
        /// </summary>
        [Fact]
        public void CanGetCorrectListOfItemsWithSemicolonsInThem2()
        {
            string projectString = @"
                <Project DefaultTargets=""Build"" ToolsVersion=""msbuilddefaulttoolsversion"">
                    <PropertyGroup>
                        <MyUserMacro>foo;bar</MyUserMacro>
                    </PropertyGroup>
                    <ItemGroup>
                        <DifferentList Include=""a"" />
                        <DifferentList Include=""b%3bc"" />
                        <DifferentList Include=""$(MyUserMacro)"" />
                    </ItemGroup>
                </Project>";
 
            using System.Xml.XmlReader reader = new System.Xml.XmlTextReader(new StringReader(projectString));
            Project project = new Project(reader);
            IEnumerable<ProjectItem> items = project.GetItems("DifferentList");
 
            Assert.Equal(4, items.Count());
            Assert.Equal("a", items.ElementAt(0).EvaluatedInclude);
            Assert.Equal("b;c", items.ElementAt(1).EvaluatedInclude);
            Assert.Equal("foo", items.ElementAt(2).EvaluatedInclude);
            Assert.Equal("bar", items.ElementAt(3).EvaluatedInclude);
        }
    }
 
#if FEATURE_COMPILE_IN_TESTS
    public class FullProjectsUsingMicrosoftCommonTargets
    {
        private readonly ITestOutputHelper _testOutput;
 
        public FullProjectsUsingMicrosoftCommonTargets(ITestOutputHelper output)
        {
            _testOutput = output;
        }
 
        private const string SolutionFileContentsWithUnusualCharacters = @"Microsoft Visual Studio Solution File, Format Version 11.00
                Project(`{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}`) = `Cons.ole;!@(foo)'^(Application1`, `Console;!@(foo)'^(Application1\Cons.ole;!@(foo)'^(Application1.csproj`, `{770F2381-8C39-49E9-8C96-0538FA4349A7}`
                EndProject
                Project(`{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}`) = `Class;!@(foo)'^(Library1`, `Class;!@(foo)'^(Library1\Class;!@(foo)'^(Library1.csproj`, `{0B4B78CC-C752-43C2-BE9A-319D20216129}`
                EndProject
                Global
                    GlobalSection(SolutionConfigurationPlatforms) = preSolution
                        Debug|Any CPU = Debug|Any CPU
                        Release|Any CPU = Release|Any CPU
                    EndGlobalSection
                    GlobalSection(ProjectConfigurationPlatforms) = postSolution
                        {770F2381-8C39-49E9-8C96-0538FA4349A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                        {770F2381-8C39-49E9-8C96-0538FA4349A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
                        {770F2381-8C39-49E9-8C96-0538FA4349A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
                        {770F2381-8C39-49E9-8C96-0538FA4349A7}.Release|Any CPU.Build.0 = Release|Any CPU
                        {0B4B78CC-C752-43C2-BE9A-319D20216129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                        {0B4B78CC-C752-43C2-BE9A-319D20216129}.Debug|Any CPU.Build.0 = Debug|Any CPU
                        {0B4B78CC-C752-43C2-BE9A-319D20216129}.Release|Any CPU.ActiveCfg = Release|Any CPU
                        {0B4B78CC-C752-43C2-BE9A-319D20216129}.Release|Any CPU.Build.0 = Release|Any CPU
                    EndGlobalSection
                    GlobalSection(SolutionProperties) = preSolution
                        HideSolutionNode = FALSE
                    EndGlobalSection
                EndGlobal
                ";
 
        /// <summary>
        ///     ESCAPING: Escaping in conditionals is broken.
        /// </summary>
        [Fact]
        public void SemicolonInConfiguration()
        {
            ObjectModelHelpers.DeleteTempProjectDirectory();
 
            // ---------------------
            // Foo.csproj
            // ---------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory("foo.csproj", $@"
                <Project DefaultTargets=`Build`>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.Common.props` />
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <TargetFrameworkVersion>{MSBuildConstants.StandardTestTargetFrameworkVersion}</TargetFrameworkVersion>
                        <OutputType>Library</OutputType>
                        <AssemblyName>ClassLibrary16</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'a%3bb%27c|AnyCPU' `>
                        <OutputPath>bin\a%3bb%27c\</OutputPath>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Compile Include=`Class1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
            ");
 
            // ---------------------
            // Class1.cs
            // ---------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory("Class1.cs", @"
                namespace ClassLibrary16
                {
                    public class Class1
                    {
                    }
                }
            ");
 
            // Create a logger.
            MockLogger logger = new MockLogger();
 
            Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory("foo.csproj");
 
            // Build the default targets using the Configuration "a;b'c".
            project.SetGlobalProperty("Configuration", EscapingUtilities.Escape("a;b'c"));
            bool success = project.Build(logger);
            Assert.True(success); // "Build failed.  See test output (Attachments in Azure Pipelines) for details"
 
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\a;b'c\ClassLibrary16.dll");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\a;b'c\ClassLibrary16.dll");
 
            logger.AssertLogContains(String.Format("foo -> {0}", Path.Combine(ObjectModelHelpers.TempProjectDir, @"bin\a;b'c\ClassLibrary16.dll")));
        }
 
        /// <summary>
        ///     ESCAPING: Escaping in conditionals is broken.
        /// </summary>
        [Fact]
        public void SemicolonInConfiguration_UsingTaskHost()
        {
            string originalOverrideTaskHostVariable = Environment.GetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC");
 
            try
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", "1");
                ObjectModelHelpers.DeleteTempProjectDirectory();
 
                // ---------------------
                // Foo.csproj
                // ---------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory("foo.csproj", $@"
                <Project DefaultTargets=`Build`>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.Common.props` />
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <TargetFrameworkVersion>{MSBuildConstants.StandardTestTargetFrameworkVersion}</TargetFrameworkVersion>
                        <OutputType>Library</OutputType>
                        <AssemblyName>ClassLibrary16</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'a%3bb%27c|AnyCPU' `>
                        <OutputPath>bin\a%3bb%27c\</OutputPath>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Compile Include=`Class1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
            ");
 
                // ---------------------
                // Class1.cs
                // ---------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory("Class1.cs", @"
                namespace ClassLibrary16
                {
                    public class Class1
                    {
                    }
                }
            ");
 
                // Create a logger.
                MockLogger logger = new MockLogger();
 
                Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory("foo.csproj");
 
                // Build the default targets using the Configuration "a;b'c".
                project.SetGlobalProperty("Configuration", EscapingUtilities.Escape("a;b'c"));
                bool success = project.Build(logger);
                Assert.True(success); // "Build failed.  See test output (Attachments in Azure Pipelines) for details"
 
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\a;b'c\ClassLibrary16.dll");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\a;b'c\ClassLibrary16.dll");
 
                logger.AssertLogContains(String.Format("foo -> {0}", Path.Combine(ObjectModelHelpers.TempProjectDir, @"bin\a;b'c\ClassLibrary16.dll")));
            }
            finally
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", originalOverrideTaskHostVariable);
            }
        }
 
        /// <summary>
        ///     ESCAPING: CopyBuildTarget target fails if the output assembly name contains a semicolon or single-quote
        /// </summary>
        [Fact]
        public void SemicolonInAssemblyName()
        {
            ObjectModelHelpers.DeleteTempProjectDirectory();
 
            // ---------------------
            // Foo.csproj
            // ---------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory("foo.csproj", $@"
                <Project DefaultTargets=`Build`>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.Common.props` />
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <TargetFrameworkVersion>{MSBuildConstants.StandardTestTargetFrameworkVersion}</TargetFrameworkVersion>
                        <OutputType>Library</OutputType>
                        <AssemblyName>Class%3bLibrary16</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <OutputPath>bin\Debug\</OutputPath>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Compile Include=`Class1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
            ");
 
            // ---------------------
            // Class1.cs
            // ---------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory("Class1.cs", @"
                namespace ClassLibrary16
                {
                    public class Class1
                    {
                    }
                }
            ");
 
            MockLogger log = new MockLogger(_testOutput);
            ObjectModelHelpers.BuildTempProjectFileExpectSuccess("foo.csproj", log);
 
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class;Library16.dll", @"Did not find expected file obj\debug\Class;Library16.dll");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class;Library16.pdb", @"Did not find expected file obj\debug\Class;Library16.pdb");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\Class;Library16.dll", @"Did not find expected file bin\debug\Class;Library16.dll");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class;Library16.pdb", @"Did not find expected file obj\debug\Class;Library16.pdb");
 
            log.AssertLogContains(String.Format("foo -> {0}", Path.Combine(ObjectModelHelpers.TempProjectDir, @"bin\Debug\Class;Library16.dll")));
        }
 
        /// <summary>
        ///     ESCAPING: CopyBuildTarget target fails if the output assembly name contains a semicolon or single-quote
        /// </summary>
        [Fact]
        public void SemicolonInAssemblyName_UsingTaskHost()
        {
            string originalOverrideTaskHostVariable = Environment.GetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC");
 
            try
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", "1");
                ObjectModelHelpers.DeleteTempProjectDirectory();
 
                // ---------------------
                // Foo.csproj
                // ---------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory("foo.csproj", $@"
                <Project DefaultTargets=`Build`>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.Common.props` />
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <TargetFrameworkVersion>{MSBuildConstants.StandardTestTargetFrameworkVersion}</TargetFrameworkVersion>
                        <OutputType>Library</OutputType>
                        <AssemblyName>Class%3bLibrary16</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <OutputPath>bin\Debug\</OutputPath>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Compile Include=`Class1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
            ");
 
                // ---------------------
                // Class1.cs
                // ---------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory("Class1.cs", @"
                namespace ClassLibrary16
                {
                    public class Class1
                    {
                    }
                }
            ");
 
                MockLogger log = new MockLogger(_testOutput);
                ObjectModelHelpers.BuildTempProjectFileExpectSuccess("foo.csproj", log);
 
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class;Library16.dll", @"Did not find expected file obj\debug\Class;Library16.dll");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class;Library16.pdb", @"Did not find expected file obj\debug\Class;Library16.pdb");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\Class;Library16.dll", @"Did not find expected file bin\debug\Class;Library16.dll");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class;Library16.pdb", @"Did not find expected file obj\debug\Class;Library16.pdb");
 
                log.AssertLogContains(String.Format("foo -> {0}", Path.Combine(ObjectModelHelpers.TempProjectDir, @"bin\Debug\Class;Library16.dll")));
            }
            finally
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", originalOverrideTaskHostVariable);
            }
        }
 
        /// <summary>
        ///     ESCAPING: Conversion Issue: Properties with $(xxx) as literals are not being converted correctly
        /// </summary>
        [Fact]
        public void DollarSignInAssemblyName()
        {
            ObjectModelHelpers.DeleteTempProjectDirectory();
 
            // ---------------------
            // Foo.csproj
            // ---------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory("foo.csproj", $@"
                <Project DefaultTargets=`Build`>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.Common.props` />
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <TargetFrameworkVersion>{MSBuildConstants.StandardTestTargetFrameworkVersion}</TargetFrameworkVersion>
                        <OutputType>Library</OutputType>
                        <AssemblyName>Class%24%28prop%29Library16</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <OutputPath>bin\Debug\</OutputPath>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Compile Include=`Class1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
            ");
 
            // ---------------------
            // Class1.cs
            // ---------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory("Class1.cs", @"
                namespace ClassLibrary16
                {
                    public class Class1
                    {
                    }
                }
            ");
 
            MockLogger log = new MockLogger(_testOutput);
            ObjectModelHelpers.BuildTempProjectFileExpectSuccess("foo.csproj", log);
 
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class$(prop)Library16.dll", @"Did not find expected file obj\debug\Class$(prop)Library16.dll");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class$(prop)Library16.pdb", @"Did not find expected file obj\debug\Class$(prop)Library16.pdb");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\Class$(prop)Library16.dll", @"Did not find expected file bin\debug\Class$(prop)Library16.dll");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\Class$(prop)Library16.pdb", @"Did not find expected file bin\debug\Class$(prop)Library16.pdb");
 
            log.AssertLogContains(String.Format("foo -> {0}", Path.Combine(ObjectModelHelpers.TempProjectDir, @"bin\Debug\Class$(prop)Library16.dll")));
        }
 
        /// <summary>
        ///     ESCAPING: Conversion Issue: Properties with $(xxx) as literals are not being converted correctly
        /// </summary>
        [Fact]
        public void DollarSignInAssemblyName_UsingTaskHost()
        {
            string originalOverrideTaskHostVariable = Environment.GetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC");
 
            try
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", "1");
                ObjectModelHelpers.DeleteTempProjectDirectory();
 
                // ---------------------
                // Foo.csproj
                // ---------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory("foo.csproj", $@"
                <Project DefaultTargets=`Build`>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.Common.props` />
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <TargetFrameworkVersion>{MSBuildConstants.StandardTestTargetFrameworkVersion}</TargetFrameworkVersion>
                        <OutputType>Library</OutputType>
                        <AssemblyName>Class%24%28prop%29Library16</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <OutputPath>bin\Debug\</OutputPath>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Compile Include=`Class1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
            ");
 
                // ---------------------
                // Class1.cs
                // ---------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory("Class1.cs", @"
                namespace ClassLibrary16
                {
                    public class Class1
                    {
                    }
                }
            ");
 
                MockLogger log = new MockLogger(_testOutput);
                ObjectModelHelpers.BuildTempProjectFileExpectSuccess("foo.csproj", log);
 
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class$(prop)Library16.dll", @"Did not find expected file obj\debug\Class$(prop)Library16.dll");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\Class$(prop)Library16.pdb", @"Did not find expected file obj\debug\Class$(prop)Library16.pdb");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\Class$(prop)Library16.dll", @"Did not find expected file bin\debug\Class$(prop)Library16.dll");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\Class$(prop)Library16.pdb", @"Did not find expected file bin\debug\Class$(prop)Library16.pdb");
 
                log.AssertLogContains(String.Format("foo -> {0}", Path.Combine(ObjectModelHelpers.TempProjectDir, @"bin\Debug\Class$(prop)Library16.dll")));
            }
            finally
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", originalOverrideTaskHostVariable);
            }
        }
 
        /// <summary>
        /// This is the case when one of the source code files in the project has a filename containing a semicolon.
        /// </summary>
        [Fact]
        public void SemicolonInSourceCodeFilename()
        {
            ObjectModelHelpers.DeleteTempProjectDirectory();
 
            // ---------------------
            // Foo.csproj
            // ---------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory("foo.csproj", $@"
                <Project DefaultTargets=`Build`>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.Common.props` />
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <TargetFrameworkVersion>{MSBuildConstants.StandardTestTargetFrameworkVersion}</TargetFrameworkVersion>
                        <OutputType>Library</OutputType>
                        <AssemblyName>ClassLibrary16</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <OutputPath>bin\Debug\</OutputPath>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Compile Include=`Class%3b1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
            ");
 
            // ---------------------
            // Class1.cs
            // ---------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory("Class;1.cs", @"
                namespace ClassLibrary16
                {
                    public class Class1
                    {
                    }
                }
            ");
 
            MockLogger log = new MockLogger(_testOutput);
            ObjectModelHelpers.BuildTempProjectFileExpectSuccess("foo.csproj", log);
 
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\ClassLibrary16.dll", @"Did not find expected file obj\debug\ClassLibrary16.dll");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\ClassLibrary16.pdb", @"Did not find expected file obj\debug\ClassLibrary16.pdb");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\ClassLibrary16.dll", @"Did not find expected file bin\debug\ClassLibrary16.dll");
            ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\ClassLibrary16.pdb", @"Did not find expected file bin\debug\ClassLibrary16.pdb");
 
            log.AssertLogContains(String.Format("foo -> {0}", Path.Combine(ObjectModelHelpers.TempProjectDir, @"bin\Debug\ClassLibrary16.dll")));
        }
 
        /// <summary>
        /// This is the case when one of the source code files in the project has a filename containing a semicolon.
        /// </summary>
        [Fact]
        public void SemicolonInSourceCodeFilename_UsingTaskHost()
        {
            string originalOverrideTaskHostVariable = Environment.GetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC");
 
            try
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", "1");
                ObjectModelHelpers.DeleteTempProjectDirectory();
 
                // ---------------------
                // Foo.csproj
                // ---------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory("foo.csproj", $@"
                <Project DefaultTargets=`Build`>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.Common.props` />
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <TargetFrameworkVersion>{MSBuildConstants.StandardTestTargetFrameworkVersion}</TargetFrameworkVersion>
                        <OutputType>Library</OutputType>
                        <AssemblyName>ClassLibrary16</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <OutputPath>bin\Debug\</OutputPath>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Compile Include=`Class%3b1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
            ");
 
                // ---------------------
                // Class1.cs
                // ---------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory("Class;1.cs", @"
                namespace ClassLibrary16
                {
                    public class Class1
                    {
                    }
                }
            ");
 
                MockLogger log = new MockLogger(_testOutput);
                ObjectModelHelpers.BuildTempProjectFileExpectSuccess("foo.csproj", log);
 
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\ClassLibrary16.dll", @"Did not find expected file obj\debug\ClassLibrary16.dll");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"obj\debug\ClassLibrary16.pdb", @"Did not find expected file obj\debug\ClassLibrary16.pdb");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\ClassLibrary16.dll", @"Did not find expected file bin\debug\ClassLibrary16.dll");
                ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(@"bin\debug\ClassLibrary16.pdb", @"Did not find expected file bin\debug\ClassLibrary16.pdb");
 
                log.AssertLogContains(String.Format("foo -> {0}", Path.Combine(ObjectModelHelpers.TempProjectDir, @"bin\Debug\ClassLibrary16.dll")));
            }
            finally
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", originalOverrideTaskHostVariable);
            }
        }
 
        /// <summary>
        /// Build a .SLN file using MSBuild.  The .SLN and the projects contained within
        /// have all sorts of different characters in their name. There
        /// is even a P2P reference between the two projects in the .SLN.
        /// </summary>
        [Fact(Skip = "This is a known issue in Roslyn. This test should be enabled if Roslyn is updated for this scenario.")]
        public void SolutionWithLotsaDifferentCharacters()
        {
            ObjectModelHelpers.DeleteTempProjectDirectory();
 
            // ---------------------------------------------------------------------
            // Console;!@(foo)'^(Application1.sln
            // ---------------------------------------------------------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory(
                @"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1.sln",
                SolutionFileContentsWithUnusualCharacters);
 
            // ---------------------------------------------------------------------
            // Console;!@(foo)'^(Application1.csproj
            // ---------------------------------------------------------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory(
                @"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1\Cons.ole;!@(foo)'^(Application1.csproj",
 
                @"
                <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion`>
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <ProductVersion>8.0.50510</ProductVersion>
                        <SchemaVersion>2.0</SchemaVersion>
                        <ProjectGuid>{770F2381-8C39-49E9-8C96-0538FA4349A7}</ProjectGuid>
                        <OutputType>Exe</OutputType>
                        <AppDesignerFolder>Properties</AppDesignerFolder>
                        <RootNamespace>Console____foo____Application1</RootNamespace>
                        <AssemblyName>Console%3b!%40%28foo%29%27^%28Application1</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <DebugSymbols>true</DebugSymbols>
                        <DebugType>full</DebugType>
                        <Optimize>false</Optimize>
                        <OutputPath>bin\Debug\</OutputPath>
                        <DefineConstants>DEBUG;TRACE</DefineConstants>
                        <ErrorReport>prompt</ErrorReport>
                        <WarningLevel>4</WarningLevel>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Release|AnyCPU' `>
                        <DebugType>pdbonly</DebugType>
                        <Optimize>true</Optimize>
                        <OutputPath>bin\Release\</OutputPath>
                        <DefineConstants>TRACE</DefineConstants>
                        <ErrorReport>prompt</ErrorReport>
                        <WarningLevel>4</WarningLevel>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Reference Include=`System.Data` />
                        <Reference Include=`System.Xml` />
                    </ItemGroup>
                    <ItemGroup>
                        <Compile Include=`Program.cs` />
                    </ItemGroup>
                    <ItemGroup>
                        <ProjectReference Include=`..\Class%3b!%40%28foo%29%27^%28Library1\Class%3b!%40%28foo%29%27^%28Library1.csproj`>
                            <Project>{0B4B78CC-C752-43C2-BE9A-319D20216129}</Project>
                            <Name>Class%3b!%40%28foo%29%27^%28Library1</Name>
                        </ProjectReference>
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
                ");
 
            // ---------------------------------------------------------------------
            // Program.cs
            // ---------------------------------------------------------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory(
                @"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1\Program.cs",
 
                @"
                using System;
                using System.Collections.Generic;
                using System.Text;
 
                namespace Console____foo____Application1
                {
                    class Program
                    {
                        static void Main(string[] args)
                        {
                            Class____foo____Library1.Class1 foo = new Class____foo____Library1.Class1();
                        }
                    }
                }
                ");
 
            // ---------------------------------------------------------------------
            // Class;!@(foo)'^(Library1.csproj
            // ---------------------------------------------------------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory(
                @"SLN;!@(foo)'^1\Class;!@(foo)'^(Library1\Class;!@(foo)'^(Library1.csproj",
 
                @"
                <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion`>
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <ProductVersion>8.0.50510</ProductVersion>
                        <SchemaVersion>2.0</SchemaVersion>
                        <ProjectGuid>{0B4B78CC-C752-43C2-BE9A-319D20216129}</ProjectGuid>
                        <OutputType>Library</OutputType>
                        <AppDesignerFolder>Properties</AppDesignerFolder>
                        <RootNamespace>Class____foo____Library1</RootNamespace>
                        <AssemblyName>Class%3b!%40%28foo%29%27^%28Library1</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <DebugSymbols>true</DebugSymbols>
                        <DebugType>full</DebugType>
                        <Optimize>false</Optimize>
                        <OutputPath>bin\Debug\</OutputPath>
                        <DefineConstants>DEBUG;TRACE</DefineConstants>
                        <ErrorReport>prompt</ErrorReport>
                        <WarningLevel>4</WarningLevel>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Release|AnyCPU' `>
                        <DebugType>pdbonly</DebugType>
                        <Optimize>true</Optimize>
                        <OutputPath>bin\Release\</OutputPath>
                        <DefineConstants>TRACE</DefineConstants>
                        <ErrorReport>prompt</ErrorReport>
                        <WarningLevel>4</WarningLevel>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Reference Include=`System.Data` />
                        <Reference Include=`System.Xml` />
                    </ItemGroup>
                    <ItemGroup>
                        <Compile Include=`Class1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
                ");
 
            // ---------------------------------------------------------------------
            // Class1.cs
            // ---------------------------------------------------------------------
            ObjectModelHelpers.CreateFileInTempProjectDirectory(
                @"SLN;!@(foo)'^1\Class;!@(foo)'^(Library1\Class1.cs",
 
                @"
                namespace Class____foo____Library1
                {
                    public class Class1
                    {
                    }
                }
                ");
 
            // Cons.ole;!@(foo)'^(Application1
            string targetForFirstProject = "Cons_ole_!__foo__^_Application1";
 
            MockLogger log = new MockLogger(_testOutput);
            ObjectModelHelpers.BuildTempProjectFileWithTargetsExpectSuccess(@"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1.sln", new string[] { targetForFirstProject }, null, log);
 
            Assert.True(File.Exists(Path.Combine(ObjectModelHelpers.TempProjectDir, @"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1\bin\debug\Console;!@(foo)'^(Application1.exe"))); // @"Did not find expected file Console;!@(foo)'^(Application1.exe"
        }
 
        /// <summary>
        /// Build a .SLN file using MSBuild.  The .SLN and the projects contained within
        /// have all sorts of different characters in their name. There
        /// is even a P2P reference between the two projects in the .SLN.
        /// </summary>
        [Fact(Skip = "This is a known issue in Roslyn. This test should be enabled if Roslyn is updated for this scenario.")]
        public void SolutionWithLotsaDifferentCharacters_UsingTaskHost()
        {
            string originalOverrideTaskHostVariable = Environment.GetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC");
 
            try
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", "1");
                ObjectModelHelpers.DeleteTempProjectDirectory();
 
                // ---------------------------------------------------------------------
                // Console;!@(foo)'^(Application1.sln
                // ---------------------------------------------------------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory(
                    @"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1.sln",
                    SolutionFileContentsWithUnusualCharacters);
 
                // ---------------------------------------------------------------------
                // Console;!@(foo)'^(Application1.csproj
                // ---------------------------------------------------------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory(
                    @"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1\Cons.ole;!@(foo)'^(Application1.csproj",
 
                    @"
                <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion`>
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <ProductVersion>8.0.50510</ProductVersion>
                        <SchemaVersion>2.0</SchemaVersion>
                        <ProjectGuid>{770F2381-8C39-49E9-8C96-0538FA4349A7}</ProjectGuid>
                        <OutputType>Exe</OutputType>
                        <AppDesignerFolder>Properties</AppDesignerFolder>
                        <RootNamespace>Console____foo____Application1</RootNamespace>
                        <AssemblyName>Console%3b!%40%28foo%29%27^%28Application1</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <DebugSymbols>true</DebugSymbols>
                        <DebugType>full</DebugType>
                        <Optimize>false</Optimize>
                        <OutputPath>bin\Debug\</OutputPath>
                        <DefineConstants>DEBUG;TRACE</DefineConstants>
                        <ErrorReport>prompt</ErrorReport>
                        <WarningLevel>4</WarningLevel>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Release|AnyCPU' `>
                        <DebugType>pdbonly</DebugType>
                        <Optimize>true</Optimize>
                        <OutputPath>bin\Release\</OutputPath>
                        <DefineConstants>TRACE</DefineConstants>
                        <ErrorReport>prompt</ErrorReport>
                        <WarningLevel>4</WarningLevel>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Reference Include=`System.Data` />
                        <Reference Include=`System.Xml` />
                    </ItemGroup>
                    <ItemGroup>
                        <Compile Include=`Program.cs` />
                    </ItemGroup>
                    <ItemGroup>
                        <ProjectReference Include=`..\Class%3b!%40%28foo%29%27^%28Library1\Class%3b!%40%28foo%29%27^%28Library1.csproj`>
                            <Project>{0B4B78CC-C752-43C2-BE9A-319D20216129}</Project>
                            <Name>Class%3b!%40%28foo%29%27^%28Library1</Name>
                        </ProjectReference>
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
                ");
 
                // ---------------------------------------------------------------------
                // Program.cs
                // ---------------------------------------------------------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory(
                    @"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1\Program.cs",
 
                    @"
                using System;
                using System.Collections.Generic;
                using System.Text;
 
                namespace Console____foo____Application1
                {
                    class Program
                    {
                        static void Main(string[] args)
                        {
                            Class____foo____Library1.Class1 foo = new Class____foo____Library1.Class1();
                        }
                    }
                }
                ");
 
                // ---------------------------------------------------------------------
                // Class;!@(foo)'^(Library1.csproj
                // ---------------------------------------------------------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory(
                    @"SLN;!@(foo)'^1\Class;!@(foo)'^(Library1\Class;!@(foo)'^(Library1.csproj",
 
                    @"
                <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion`>
                    <PropertyGroup>
                        <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
                        <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
                        <ProductVersion>8.0.50510</ProductVersion>
                        <SchemaVersion>2.0</SchemaVersion>
                        <ProjectGuid>{0B4B78CC-C752-43C2-BE9A-319D20216129}</ProjectGuid>
                        <OutputType>Library</OutputType>
                        <AppDesignerFolder>Properties</AppDesignerFolder>
                        <RootNamespace>Class____foo____Library1</RootNamespace>
                        <AssemblyName>Class%3b!%40%28foo%29%27^%28Library1</AssemblyName>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
                        <DebugSymbols>true</DebugSymbols>
                        <DebugType>full</DebugType>
                        <Optimize>false</Optimize>
                        <OutputPath>bin\Debug\</OutputPath>
                        <DefineConstants>DEBUG;TRACE</DefineConstants>
                        <ErrorReport>prompt</ErrorReport>
                        <WarningLevel>4</WarningLevel>
                    </PropertyGroup>
                    <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Release|AnyCPU' `>
                        <DebugType>pdbonly</DebugType>
                        <Optimize>true</Optimize>
                        <OutputPath>bin\Release\</OutputPath>
                        <DefineConstants>TRACE</DefineConstants>
                        <ErrorReport>prompt</ErrorReport>
                        <WarningLevel>4</WarningLevel>
                    </PropertyGroup>
                    <ItemGroup>
                        <Reference Include=`System` />
                        <Reference Include=`System.Data` />
                        <Reference Include=`System.Xml` />
                    </ItemGroup>
                    <ItemGroup>
                        <Compile Include=`Class1.cs` />
                    </ItemGroup>
                    <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
                </Project>
                ");
 
                // ---------------------------------------------------------------------
                // Class1.cs
                // ---------------------------------------------------------------------
                ObjectModelHelpers.CreateFileInTempProjectDirectory(
                    @"SLN;!@(foo)'^1\Class;!@(foo)'^(Library1\Class1.cs",
 
                    @"
                namespace Class____foo____Library1
                {
                    public class Class1
                    {
                    }
                }
                ");
 
                // Cons.ole;!@(foo)'^(Application1
                string targetForFirstProject = "Cons_ole_!__foo__^_Application1";
 
                MockLogger log = new MockLogger(_testOutput);
                ObjectModelHelpers.BuildTempProjectFileWithTargetsExpectSuccess(@"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1.sln", new string[] { targetForFirstProject }, null, log);
 
                Assert.True(File.Exists(Path.Combine(ObjectModelHelpers.TempProjectDir, @"SLN;!@(foo)'^1\Console;!@(foo)'^(Application1\bin\debug\Console;!@(foo)'^(Application1.exe"))); // @"Did not find expected file Console;!@(foo)'^(Application1.exe"
            }
            finally
            {
                Environment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", originalOverrideTaskHostVariable);
            }
        }
    }
#endif
 
    internal sealed class EscapingInProjectsHelper
    {
        /// <summary>
        /// Deletes all *.weirdo files from the temp path, and dumps 3 files there --
        /// a.weirdo, b.weirdo, c.weirdo.  This is so that we can exercise our wildcard
        /// matching a little bit without having to plumb mock objects all the way through
        /// the engine.
        /// </summary>
        internal static void CreateThreeWeirdoFiles()
        {
            ObjectModelHelpers.DeleteTempProjectDirectory();
 
            // Create 3 files in the temp path -- a.weirdo, b.weirdo, and c.weirdo.
            File.WriteAllText(Path.Combine(ObjectModelHelpers.TempProjectDir, "a.weirdo"), String.Empty);
            File.WriteAllText(Path.Combine(ObjectModelHelpers.TempProjectDir, "b.weirdo"), String.Empty);
            File.WriteAllText(Path.Combine(ObjectModelHelpers.TempProjectDir, "c.weirdo"), String.Empty);
        }
 
        /// <summary>
        /// Given a project and an item type, gets the items of that type, and renames an item
        /// with the old evaluated include to have the new evaluated include instead.
        /// </summary>
        /// <param name="project"></param>
        /// <param name="itemType"></param>
        /// <param name="oldEvaluatedInclude"></param>
        /// <param name="newEvaluatedInclude"></param>
        internal static IEnumerable<ProjectItem> ModifyItemOfTypeInProject(Project project, string itemType, string oldEvaluatedInclude, string newEvaluatedInclude)
        {
            IEnumerable<ProjectItem> itemsToMatch = project.GetItems(itemType);
            List<ProjectItem> matchingItems = new List<ProjectItem>();
 
            foreach (ProjectItem item in itemsToMatch)
            {
                if (String.Equals(item.EvaluatedInclude, oldEvaluatedInclude, StringComparison.OrdinalIgnoreCase))
                {
                    matchingItems.Add(item);
                }
            }
 
            for (int i = 0; i < matchingItems.Count; i++)
            {
                matchingItems[i].Rename(newEvaluatedInclude);
            }
 
            return matchingItems;
        }
 
        /// <summary>
        /// Helper for SpecialCharactersInMetadataValue tests
        /// </summary>
        internal static void SpecialCharactersInMetadataValueTests(ProjectItem item)
        {
            Assert.Equal("%3B", item.GetMetadata("EscapedSemicolon").UnevaluatedValue);
            Assert.Equal("%3B", item.GetMetadata("EscapedSemicolon").EvaluatedValueEscaped);
            Assert.Equal(";", item.GetMetadata("EscapedSemicolon").EvaluatedValue);
            Assert.Equal("%3B", Project.GetMetadataValueEscaped(item, "EscapedSemicolon"));
            Assert.Equal(";", item.GetMetadataValue("EscapedSemicolon"));
 
            Assert.Equal("%24", item.GetMetadata("EscapedDollarSign").UnevaluatedValue);
            Assert.Equal("%24", item.GetMetadata("EscapedDollarSign").EvaluatedValueEscaped);
            Assert.Equal("$", item.GetMetadata("EscapedDollarSign").EvaluatedValue);
            Assert.Equal("%24", Project.GetMetadataValueEscaped(item, "EscapedDollarSign"));
            Assert.Equal("$", item.GetMetadataValue("EscapedDollarSign"));
        }
    }
}