File: CodeTaskFactoryTests.cs
Web Access
Project: ..\..\..\src\Tasks.UnitTests\Microsoft.Build.Tasks.UnitTests.csproj (Microsoft.Build.Tasks.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.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Utilities;
using Xunit;
 
#nullable disable
 
namespace Microsoft.Build.UnitTests
{
#if FEATURE_CODETASKFACTORY

    using System.CodeDom.Compiler;
    using Microsoft.Build.Tasks.UnitTests;
 
    public sealed class CodeTaskFactoryTests
    {
        /// <summary>
        /// Test the simple case where we have a string parameter and we want to log that.
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactory()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                     Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory Text=`Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Hello, World!");
        }
 
        /// <summary>
        /// Test the simple case where we have a string parameter and we want to log that.
        /// Specifically testing that even when the ToolsVersion is post-4.0, and thus
        /// Microsoft.Build.Tasks.v4.0.dll is expected to NOT be in MSBuildToolsPath, that
        /// we will redirect under the covers to use the current tasks instead.
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactory_RedirectFrom4()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                     Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory Text=`Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Hello, World!");
            mockLogger.AssertLogDoesntContain("Microsoft.Build.Tasks.v4.0.dll");
        }
 
        /// <summary>
        /// Test the simple case where we have a string parameter and we want to log that.
        /// Specifically testing that even when the ToolsVersion is post-12.0, and thus
        /// Microsoft.Build.Tasks.v12.0.dll is expected to NOT be in MSBuildToolsPath, that
        /// we will redirect under the covers to use the current tasks instead.
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactory_RedirectFrom12()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                     Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory Text=`Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Hello, World!");
            mockLogger.AssertLogDoesntContain("Microsoft.Build.Tasks.v12.0.dll");
        }
 
        /// <summary>
        /// Test the simple case where we have a string parameter and we want to log that.
        /// Specifically testing that even when the ToolsVersion is post-4.0, and we have redirection
        /// logic in place for the AssemblyFile case to deal with Microsoft.Build.Tasks.v4.0.dll not
        /// being in MSBuildToolsPath anymore, that this does NOT affect full fusion AssemblyNames --
        /// it's picked up from the GAC, where it is anyway, so there's no need to redirect.
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactory_NoAssemblyNameRedirect()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory` TaskFactory=`CodeTaskFactory` AssemblyName=`Microsoft.Build.Tasks.Core, Version=15.1.0.0` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                     Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory Text=`Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Hello, World!");
            mockLogger.AssertLogContains("Microsoft.Build.Tasks.Core, Version=15.1.0.0");
        }
 
        /// <summary>
        /// Test the simple case where we have a string parameter and we want to log that.
        /// </summary>
        [Fact]
        public void VerifyRequiredAttribute()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_VerifyRequiredAttribute` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text Required='true'/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                     Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_VerifyRequiredAttribute/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            mockLogger.AssertLogContains("MSB4044");
        }
 
        /// <summary>
        /// Verify we get an error if a runtime exception is logged
        /// </summary>
        [Fact]
        public void RuntimeException()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_RuntimeException` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                     throw new InvalidOperationException(""MyCustomException"");
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_RuntimeException/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, true);
            mockLogger.AssertLogContains("MSB4018");
            mockLogger.AssertLogContains("MyCustomException");
        }
 
        /// <summary>
        /// Verify we get an error if a the languages attribute is set but it is empty
        /// </summary>
        [Fact]
        public void EmptyLanguage()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_EmptyLanguage` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code Language=''>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_EmptyLanguage/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.AttributeEmpty");
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Language"));
        }
 
        /// <summary>
        /// Verify we get an error if a the Type attribute is set but it is empty
        /// </summary>
        [Fact]
        public void EmptyType()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_EmptyType` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code Type=''>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_EmptyType/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.AttributeEmpty");
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Type"));
        }
 
        /// <summary>
        /// Verify we get an error if a the source attribute is set but it is empty
        /// </summary>
        [Fact]
        public void EmptySource()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_EmptySource` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code Source=''>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_EmptySource/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.AttributeEmpty");
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Source"));
        }
 
        /// <summary>
        /// Verify we get an error if a reference is missing an include attribute is set but it is empty
        /// </summary>
        [Theory]
        [InlineData("")]
        [InlineData("Include=\"\"")]
        [InlineData("Include=\" \"")]
        public void EmptyReferenceInclude(string includeSetting)
        {
            string taskName = "CustomTaskFromCodeFactory_EmptyReferenceInclude";
            string projectFileContents = @$"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`{taskName}` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                 <Reference {includeSetting}/>
                                <Code>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <{taskName}/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.AttributeEmptyWithTaskElement");
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Include", "Reference", taskName));
        }
 
        /// <summary>
        /// Verify we get an error if a Using statement is missing an namespace attribute is set but it is empty
        /// </summary>
        [Fact]
        public void EmptyUsingNamespace()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_EmptyUsingNamespace` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                 <Using/>
                                <Code>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_EmptyUsingNamespace/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.AttributeEmpty");
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Namespace"));
        }
 
        /// <summary>
        /// Verify we get pass even if the reference is not a full path
        /// </summary>
        [Fact]
        public void ReferenceNotPath()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_ReferenceNotPath` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                 <Reference Include='System'/>
                                <Code>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_ReferenceNotPath Text=""Hello""/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Hello");
        }
 
        /// <summary>
        /// Verify we get an error a reference has strange chars
        /// </summary>
        [Fact]
        public void ReferenceInvalidChars()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_ReferenceInvalidChars` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                 <Reference Include='@@#$@#'/>
                                <Code>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_ReferenceInvalidChars/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            mockLogger.AssertLogContains("MSB3755");
            mockLogger.AssertLogContains("@@#$@#");
        }
 
        /// <summary>
        /// Verify we get an error if a using has invalid chars
        /// </summary>
        [Fact]
        public void UsingInvalidChars()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_UsingInvalidChars` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                 <Using Namespace='@@#$@#'/>
                                <Code>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_UsingInvalidChars/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            mockLogger.AssertLogContains("CS1646");
        }
 
        /// <summary>
        /// Verify we get an error if the sources points to an invalid file
        /// </summary>
        [Fact]
        public void SourcesInvalidFile()
        {
            string tempFileName = "Moose_" + Guid.NewGuid().ToString() + ".cs";
 
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_SourcesInvalidFile` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code Source='$(SystemDrive)\\" + tempFileName + @"'>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_SourcesInvalidFile/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            mockLogger.AssertLogContains(Environment.GetEnvironmentVariable("SystemDrive") + '\\' + tempFileName);
        }
 
        /// <summary>
        /// Verify we get an error if a the code element is missing
        /// </summary>
        [Fact]
        public void MissingCodeElement()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_MissingCodeElement` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_MissingCodeElement/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            mockLogger.AssertLogContains(String.Format(ResourceUtilities.GetResourceString("CodeTaskFactory.CodeElementIsMissing"), "CustomTaskFromCodeFactory_MissingCodeElement"));
        }
 
        /// <summary>
        /// Test the case where we have adding a using statement
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactoryTestExtraUsing()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactoryTestExtraUsing` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Using Namespace='System.Linq.Expressions'/>
                                <Code>
                                      string linqString = ExpressionType.Add.ToString();
                                     Log.LogMessage(MessageImportance.High, linqString + Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactoryTestExtraUsing Text=`:Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            string linqString = nameof(System.Linq.Expressions.ExpressionType.Add);
            mockLogger.AssertLogContains(linqString + ":Hello, World!");
        }
 
        /// <summary>
        /// Verify setting the output tag on the parameter causes it to be an output from the perspective of the targets
        /// </summary>
        [Fact]
        public void BuildTaskDateCodeFactory()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`DateTaskFromCodeFactory_BuildTaskDateCodeFactory` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll`>
                            <ParameterGroup>
                               <CurrentDate ParameterType=`System.String` Output=`true` />
                            </ParameterGroup>
                            <Task>
                                <Code>
                                    CurrentDate = DateTime.Now.ToString();
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <DateTaskFromCodeFactory_BuildTaskDateCodeFactory>
                                <Output TaskParameter=`CurrentDate` PropertyName=`CurrentDate` />
                            </DateTaskFromCodeFactory_BuildTaskDateCodeFactory>
                            <Message Text=`Current Date and Time: [[$(CurrentDate)]]` Importance=`High` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Current Date and Time:");
            mockLogger.AssertLogDoesntContain("[[]]");
        }
 
        /// <summary>
        /// Verify that the vb language works and that creating the execute method also works
        /// </summary>
        [Fact]
        public void MethodImplmentationVB()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CodeMethod_MethodImplmentationVB` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll`>
                        <ParameterGroup>
                            <Text ParameterType='System.String' />
                        </ParameterGroup>
                        <Task>
                            <Code Type='Method' Language='vb'>
 <![CDATA[
                             Public Overrides Function Execute() As Boolean
                                 Log.LogMessage(MessageImportance.High, Text)
                                 Return True
                             End Function
  ]]>
                            </Code>
                         </Task>
                         </UsingTask>
                        <Target Name=`Build`>
                            <CodeMethod_MethodImplmentationVB Text='IAMVBTEXT'/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("IAMVBTEXT");
        }
 
        /// <summary>
        /// Verify that System does not need to be passed in as a extra reference when targeting vb
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactoryTestSystemVB()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactoryTestSystemVB` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code Language='vb'>
                                     Dim headerRequest As String
                                     headerRequest = System.Net.HttpRequestHeader.Accept.ToString()
                                     Log.LogMessage(MessageImportance.High, headerRequest + Text)
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactoryTestSystemVB Text=`:Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Accept" + ":Hello, World!");
        }
 
        /// <summary>
        /// Verify that System does not need to be passed in as a extra reference when targeting c#
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactoryTestSystemCS()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactoryTestSystemCS` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code Language='cs'>
                                     string headerRequest = System.Net.HttpRequestHeader.Accept.ToString();
                                     Log.LogMessage(MessageImportance.High, headerRequest + Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactoryTestSystemCS Text=`:Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Accept" + ":Hello, World!");
        }
 
        /// <summary>
        /// Make sure we can pass in extra references than the automatic ones. For example the c# compiler does not pass in
        /// system.dll. So lets test that case
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactoryTestExtraReferenceCS()
        {
            string netFrameworkDirectory = ToolLocationHelper.GetPathToDotNetFrameworkReferenceAssemblies(TargetDotNetFrameworkVersion.Version45);
            if (netFrameworkDirectory == null)
            {
                // "CouldNotFindRequiredTestDirectory"
                return;
            }
 
            string systemNETLocation = Path.Combine(netFrameworkDirectory, "System.Net.dll");
 
            if (!File.Exists(systemNETLocation))
            {
                // "CouldNotFindRequiredTestFile"
                return;
            }
 
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactoryTestExtraReferenceCS` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Using Namespace='System.Net'/>
                                <Reference Include='" + systemNETLocation + @"'/>
                                <Code>
                                     string netString = System.Net.HttpStatusCode.OK.ToString();
                                     Log.LogMessage(MessageImportance.High, netString + Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactoryTestExtraReferenceCS Text=`:Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("OK" + ":Hello, World!");
        }
 
        /// <summary>
        /// jscript .net works
        /// </summary>
        [Fact]
        public void MethodImplementationJScriptNet()
        {
            if (!CodeDomProvider.IsDefinedLanguage("js"))
            {
                // "JScript .net Is not installed on the test machine this test cannot run"
                return;
            }
 
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CodeMethod_MethodImplementationJScriptNet` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll`>
                        <ParameterGroup>
                            <Text ParameterType='System.String' />
                        </ParameterGroup>
                        <Task>
                            <Code Type='Method' Language='js'>
 <![CDATA[
                             override function Execute() : System.Boolean
                             {
                                 Log.LogMessage(MessageImportance.High, Text);
                                 return true;
                             }
  ]]>
                            </Code>
                         </Task>
                         </UsingTask>
                        <Target Name=`Build`>
                            <CodeMethod_MethodImplementationJScriptNet Text='IAMJSTEXT'/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("IAMJSTEXT");
        }
 
        /// <summary>
        /// Verify we can set a code type of Method which expects us to override the execute method entirely.
        /// </summary>
        [Fact]
        public void MethodImplementation()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CodeMethod_MethodImplementation` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll`>
                        <ParameterGroup>
                            <Text ParameterType='System.String' />
                        </ParameterGroup>
                        <Task>
                            <Code Type='Method'>
 <![CDATA[
                                public override bool Execute()
                                {
                                    Log.LogMessage(MessageImportance.High, Text);
                                    return true;
                                }
  ]]>
                            </Code>
                         </Task>
                         </UsingTask>
                        <Target Name=`Build`>
                            <CodeMethod_MethodImplementation Text='IAMTEXT'/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("IAMTEXT");
        }
 
        /// <summary>
        /// Verify we can set the type to Class and this expects an entire class to be entered into the code tag
        /// </summary>
        [Fact]
        public void ClassImplementationTest()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`LogNameValue_ClassImplementationTest` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll`>
                        <ParameterGroup>
                            <Name ParameterType='System.String' />
                            <Value ParameterType='System.String' />
                        </ParameterGroup>
                        <Task>
                            <Code Type='Class'>
 <![CDATA[
                    using System;
                    using System.Collections.Generic;
                    using System.Text;
                    using Microsoft.Build.Utilities;
                    using Microsoft.Build.Framework;
 
                    namespace Microsoft.Build.NonShippingTasks
                    {
                        public class LogNameValue_ClassImplementationTest : Task
                        {
                            private string variableName;
                            private string variableValue;
 
 
                            [Required]
                            public string Name
                            {
                                get { return variableName; }
                                set { variableName = value; }
                            }
 
 
                            public string Value
                            {
                                get { return variableValue; }
                                set { variableValue = value; }
                            }
 
 
                            public override bool Execute()
                            {
                                // Set the process environment
                                Log.LogMessage(""Setting {0}={1}"", this.variableName, this.variableValue);
                                return true;
                            }
                        }
                    }
  ]]>
                            </Code>
                         </Task>
                         </UsingTask>
                        <Target Name=`Build`>
                            <LogNameValue_ClassImplementationTest Name='MyName' Value='MyValue'/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("MyName=MyValue");
        }
 
        /// <summary>
        /// Verify we can set the type to Class and this expects an entire class to be entered into the code tag
        /// </summary>
        [Fact]
        public void ClassImplementationTestDoesNotInheritFromITask()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`ClassImplementationTestDoesNotInheritFromITask` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll`>
                        <ParameterGroup>
                            <Name ParameterType='System.String' />
                            <Value ParameterType='System.String' />
                        </ParameterGroup>
                        <Task>
                            <Code Type='Class'>
 <![CDATA[
                    using System;
                    using System.Collections.Generic;
                    using System.Text;
                    using Microsoft.Build.Utilities;
                    using Microsoft.Build.Framework;
 
                    namespace Microsoft.Build.NonShippingTasks
                    {
                        public class ClassImplementationTestDoesNotInheritFromITask
                        {
                            private string variableName;
 
                            [Required]
                            public string Name
                            {
                                get { return variableName; }
                                set { variableName = value; }
                            }
 
                            public bool Execute()
                            {
                                // Set the process environment
                                Console.Out.WriteLine(variableName);
                                return true;
                            }
                        }
                    }
  ]]>
                            </Code>
                         </Task>
                         </UsingTask>
                        <Target Name=`Build`>
                            <ClassImplementationTestDoesNotInheritFromITask Name='MyName' Value='MyValue'/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.NeedsITaskInterface");
            mockLogger.AssertLogContains(unformattedMessage);
        }
 
        /// <summary>
        /// Verify we get an error if a the Type attribute is set but it is empty
        /// </summary>
        [Fact]
        public void MultipleCodeElements()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_EmptyType` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                                <Code>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_EmptyType/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.MultipleCodeNodes");
            mockLogger.AssertLogContains(unformattedMessage);
        }
 
        /// <summary>
        /// Verify we get an error if a the Type attribute is set but it is empty
        /// </summary>
        [Fact]
        public void ReferenceNestedInCode()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_EmptyType` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                      <Reference Include=""System.Xml""/>
                                       <Using Namespace=""Hello""/>
                                        <Task/>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_EmptyType/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.InvalidElementLocation");
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Reference", "Code"));
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Using", "Code"));
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Task", "Code"));
        }
 
        /// <summary>
        /// Verify we get an error if there is an unknown element in the task tag
        /// </summary>
        [Fact]
        public void UnknownElementInTask()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_EmptyType` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                              <Unknown/>
                                <Code>
                                       Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_EmptyType Text=""HELLO""/>
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, false);
            string unformattedMessage = ResourceUtilities.GetResourceString("CodeTaskFactory.InvalidElementLocation");
            mockLogger.AssertLogContains(String.Format(unformattedMessage, "Unknown", "Task"));
        }
 
        /// <summary>
        /// Verify we can set a source file location and this will be read in and used.
        /// </summary>
        [Fact]
        public void ClassSourcesTest()
        {
            string sourceFileContent = @"
                                       using System;
                    using System.Collections.Generic;
                    using System.Text;
                    using Microsoft.Build.Utilities;
                    using Microsoft.Build.Framework;
 
                    namespace Microsoft.Build.NonShippingTasks
                    {
                        public class LogNameValue_ClassSourcesTest : Task
                        {
                            private string variableName;
                            private string variableValue;
 
 
                            [Required]
                            public string Name
                            {
                                get { return variableName; }
                                set { variableName = value; }
                            }
 
 
                            public string Value
                            {
                                get { return variableValue; }
                                set { variableValue = value; }
                            }
 
 
                            public override bool Execute()
                            {
                                // Set the process environment
                                Log.LogMessage(""Setting {0}={1}"", this.variableName, this.variableValue);
                                return true;
                            }
                        }
                    }
";
 
            string tempFileDirectory = Path.GetTempPath();
            string tempFileName = Guid.NewGuid().ToString() + ".cs";
            string tempSourceFile = Path.Combine(tempFileDirectory, tempFileName);
            File.WriteAllText(tempSourceFile, sourceFileContent);
 
            try
            {
                string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`LogNameValue_ClassSourcesTest` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll`>
                        <ParameterGroup>
                            <Name ParameterType='System.String' />
                            <Value ParameterType='System.String' />
                        </ParameterGroup>
                        <Task>
                            <Code Source='" + tempSourceFile + @"'/>
                         </Task>
                         </UsingTask>
                        <Target Name=`Build`>
                            <LogNameValue_ClassSourcesTest Name='MyName' Value='MyValue'/>
                        </Target>
                    </Project>";
 
                MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
                mockLogger.AssertLogContains("MyName=MyValue");
            }
            finally
            {
                if (File.Exists(tempSourceFile))
                {
                    File.Delete(tempSourceFile);
                }
            }
        }
 
        /// <summary>
        /// Code factory test where the TMP directory does not exist.
        /// See https://github.com/dotnet/msbuild/issues/328 for details.
        /// </summary>
        [Fact]
        public void BuildTaskSimpleCodeFactoryTempDirectoryDoesntExist()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                     Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory Text=`Hello, World!` />
                        </Target>
                    </Project>";
 
            var oldTempPath = Environment.GetEnvironmentVariable("TMP");
            var newTempPath = Path.Combine(Path.GetFullPath(oldTempPath), Path.GetRandomFileName());
 
            try
            {
                // Ensure we're getting the right temp path (%TMP% == GetTempPath())
                Assert.Equal(
                    FileUtilities.EnsureTrailingSlash(Path.GetTempPath()),
                    FileUtilities.EnsureTrailingSlash(Path.GetFullPath(oldTempPath)));
                Assert.False(Directory.Exists(newTempPath));
 
                Environment.SetEnvironmentVariable("TMP", newTempPath);
 
                Assert.Equal(
                    FileUtilities.EnsureTrailingSlash(newTempPath),
                    FileUtilities.EnsureTrailingSlash(Path.GetTempPath()));
 
                MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
                mockLogger.AssertLogContains("Hello, World!");
            }
            finally
            {
                Environment.SetEnvironmentVariable("TMP", oldTempPath);
                FileUtilities.DeleteDirectoryNoThrow(newTempPath, true);
            }
        }
 
        /// <summary>
        /// Test the simple case where we have a string parameter and we want to log that.
        /// </summary>
        [Fact]
        public void RedundantMSBuildReferences()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_RedundantMSBuildReferences` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                              <Reference Include='$(MSBuildToolsPath)\Microsoft.Build.Framework.dll' />
                              <Reference Include='$(MSBuildToolsPath)\Microsoft.Build.Utilities.Core.dll' />
 
                                <Code>
                                     Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_RedundantMSBuildReferences Text=`Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents);
            mockLogger.AssertLogContains("Hello, World!");
        }
 
        [Fact]
        public void EmbedsGeneratedFromSourceFileInBinlog()
        {
            string taskName = "HelloTask";
 
            string sourceContent = $$"""
                namespace InlineTask
                {
                    using Microsoft.Build.Utilities;
 
                    public class {{taskName}} : Task
                    {
                        public override bool Execute()
                        {
                            Log.LogMessage("Hello, world!");
                            return !Log.HasLoggedErrors;
                        }
                    }
                }
                """;
 
            CodeTaskFactoryEmbeddedFileInBinlogTestHelper.BuildFromSourceAndCheckForEmbeddedFileInBinlog(
                FactoryType.CodeTaskFactory, taskName, sourceContent, true);
        }
 
        [Fact]
        public void EmbedsGeneratedFromSourceFileInBinlogWhenFailsToCompile()
        {
            string taskName = "HelloTask";
 
            string sourceContent = $$"""
                namespace InlineTask
                {
                    using Microsoft.Build.Utilities;
 
                    public class {{taskName}} : Task
                    {
                """;
 
            CodeTaskFactoryEmbeddedFileInBinlogTestHelper.BuildFromSourceAndCheckForEmbeddedFileInBinlog(
                FactoryType.CodeTaskFactory, taskName, sourceContent, false);
        }
 
        [Fact]
        public void EmbedsGeneratedFileInBinlog()
        {
            string taskXml = @"
                <Task>
                    <Code Type=""Fragment"" Language=""cs"">
                        <![CDATA[
                              Log.LogMessage(""Hello, World!"");
                		   ]]>
                    </Code>
                </Task>";
 
            CodeTaskFactoryEmbeddedFileInBinlogTestHelper.BuildAndCheckForEmbeddedFileInBinlog(
                FactoryType.CodeTaskFactory, "HelloTask", taskXml, true);
        }
 
        [Fact]
        public void EmbedsGeneratedFileInBinlogWhenFailsToCompile()
        {
            string taskXml = @"
                <Task>
                    <Code Type=""Fragment"" Language=""cs"">
                        <![CDATA[
                              Log.LogMessage(""Hello, World!
                		   ]]>
                    </Code>
                </Task>";
 
            CodeTaskFactoryEmbeddedFileInBinlogTestHelper.BuildAndCheckForEmbeddedFileInBinlog(
                FactoryType.CodeTaskFactory, "HelloTask", taskXml, false);
        }
    }
#else
    public sealed class CodeTaskFactoryTests
    {
        [Fact]
        public void CodeTaskFactoryNotSupported()
        {
            string projectFileContents = @"
                    <Project ToolsVersion='msbuilddefaulttoolsversion'>
                        <UsingTask TaskName=`CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory` TaskFactory=`CodeTaskFactory` AssemblyFile=`$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll` >
                         <ParameterGroup>
                             <Text/>
                          </ParameterGroup>
                            <Task>
                                <Code>
                                     Log.LogMessage(MessageImportance.High, Text);
                                </Code>
                            </Task>
                        </UsingTask>
                        <Target Name=`Build`>
                            <CustomTaskFromCodeFactory_BuildTaskSimpleCodeFactory Text=`Hello, World!` />
                        </Target>
                    </Project>";
 
            MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectFailure(projectFileContents, allowTaskCrash: false);
 
            BuildErrorEventArgs error = mockLogger.Errors.FirstOrDefault();
 
            Assert.NotNull(error);
            Assert.Equal("MSB4801", error.Code);
            Assert.Contains("CodeTaskFactory", error.Message);
        }
    }
#endif
}