File: src\Compilers\Core\MSBuildTaskTests\TestUtilities\IntegrationTestBase.cs
Web Access
Project: src\src\Compilers\Core\SdkTaskTests\Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj (Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests;
 
public abstract class IntegrationTestBase : TestBase
{
    protected static readonly string? s_msbuildDirectory;
 
#if NET472
    static IntegrationTestBase()
    {
        s_msbuildDirectory = DesktopTestHelpers.GetMSBuildDirectory();
    }
#endif
 
    protected readonly ITestOutputHelper _output;
    protected readonly string? _msbuildExecutable;
    protected readonly TempDirectory _tempDirectory;
    protected string _buildTaskDll;
 
    protected IntegrationTestBase(ITestOutputHelper output)
    {
        if (s_msbuildDirectory == null)
        {
            output.WriteLine("Could not locate MSBuild");
        }
 
        _output = output;
        _msbuildExecutable = s_msbuildDirectory == null ? null : Path.Combine(s_msbuildDirectory, "MSBuild.exe");
        _tempDirectory = Temp.CreateDirectory();
        _buildTaskDll = typeof(ManagedCompiler).Assembly.Location;
    }
 
    private static IEnumerable<KeyValuePair<string, string>> AddForLoggingEnvironmentVars(IEnumerable<KeyValuePair<string, string>>? vars)
    {
        vars = vars ?? new KeyValuePair<string, string>[] { };
        if (!vars.Where(kvp => kvp.Key == "RoslynCommandLineLogFile").Any())
        {
            var list = vars.ToList();
            list.Add(new KeyValuePair<string, string>(
                "RoslynCommandLineLogFile",
                typeof(IntegrationTestBase).Assembly.Location + ".client-server.log"));
            return list;
        }
        return vars;
    }
 
    protected static ProcessResult RunCommandLineCompiler(
        string compilerPath,
        string arguments,
        string currentDirectory,
        IEnumerable<KeyValuePair<string, string>>? additionalEnvironmentVars = null)
    {
        return ProcessUtilities.Run(
            compilerPath,
            arguments,
            currentDirectory,
            additionalEnvironmentVars: AddForLoggingEnvironmentVars(additionalEnvironmentVars));
    }
 
    protected static ProcessResult RunCommandLineCompiler(
        string compilerPath,
        string arguments,
        TempDirectory currentDirectory,
        IEnumerable<KeyValuePair<string, string>> filesInDirectory,
        IEnumerable<KeyValuePair<string, string>>? additionalEnvironmentVars = null)
    {
        foreach (var pair in filesInDirectory)
        {
            TempFile file = currentDirectory.CreateFile(pair.Key);
            file.WriteAllText(pair.Value);
        }
 
        return RunCommandLineCompiler(
            compilerPath,
            arguments,
            currentDirectory.Path,
            additionalEnvironmentVars: AddForLoggingEnvironmentVars(additionalEnvironmentVars));
    }
 
    protected ProcessResult? RunMsbuild(
        string arguments,
        TempDirectory currentDirectory,
        IEnumerable<KeyValuePair<string, string>> filesInDirectory,
        IEnumerable<KeyValuePair<string, string>>? additionalEnvironmentVars = null)
    {
        if (_msbuildExecutable != null)
        {
            return RunCommandLineCompiler(
                _msbuildExecutable,
                arguments,
                currentDirectory,
                filesInDirectory,
                additionalEnvironmentVars);
        }
 
        if (ExecutionConditionUtil.IsDesktop)
        {
            _output.WriteLine("Skipping because Framework MSBuild is missing, this is a desktop test, " +
                "and we cannot use the desktop Csc/Vbc task from 'dotnet msbuild', i.e., Core MSBuild.");
            return null;
        }
 
        return RunCommandLineCompiler(
            "dotnet",
            $"msbuild {arguments}",
            currentDirectory,
            filesInDirectory,
            additionalEnvironmentVars);
    }
 
    [Theory, CombinatorialData]
    public void SdkBuild_Csc(bool useSharedCompilation)
    {
        var result = RunMsbuild(
            "/v:n /m /nr:false /t:Build /restore Test.csproj",
            _tempDirectory,
            new Dictionary<string, string>
            {
                { "File.cs", """
                    class Program { static void Main() { System.Console.WriteLine("Hello from file"); } }
                    """ },
                { "Test.csproj", $"""
                    <Project Sdk="Microsoft.NET.Sdk">
                        <UsingTask TaskName="Microsoft.CodeAnalysis.BuildTasks.Csc" AssemblyFile="{_buildTaskDll}" />
                        <PropertyGroup>
                            <TargetFramework>netstandard2.0</TargetFramework>
                            <UseSharedCompilation>{useSharedCompilation}</UseSharedCompilation>
                        </PropertyGroup>
                    </Project>
                    """ },
            });
 
        if (result == null) return;
 
        _output.WriteLine(result.Output);
 
        Assert.Equal(0, result.ExitCode);
        Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output);
        Assert.DoesNotContain("csc.dll", result.Output);
        Assert.Contains(ExecutionConditionUtil.IsWindows ? "csc.exe" : "csc", result.Output);
    }
 
    [Theory, CombinatorialData]
    public void SdkBuild_Vbc(bool useSharedCompilation)
    {
        var result = RunMsbuild(
            "/v:n /m /nr:false /t:Build /restore Test.vbproj",
            _tempDirectory,
            new Dictionary<string, string>
            {
                { "File.vb", """
                    Public Module Program
                        Public Sub Main()
                            System.Console.WriteLine("Hello from file")
                        End Sub
                    End Module
                    """ },
                { "Test.vbproj", $"""
                    <Project Sdk="Microsoft.NET.Sdk">
                        <UsingTask TaskName="Microsoft.CodeAnalysis.BuildTasks.Vbc" AssemblyFile="{_buildTaskDll}" />
                        <PropertyGroup>
                            <TargetFramework>netstandard2.0</TargetFramework>
                            <UseSharedCompilation>{useSharedCompilation}</UseSharedCompilation>
                        </PropertyGroup>
                    </Project>
                    """ },
            });
 
        if (result == null) return;
 
        _output.WriteLine(result.Output);
 
        Assert.Equal(0, result.ExitCode);
        Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output);
        Assert.DoesNotContain("vbc.dll", result.Output);
        Assert.Contains(ExecutionConditionUtil.IsWindows ? "vbc.exe" : "vbc", result.Output);
    }
 
    [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79907")]
    public void StdLib_Csc(bool useSharedCompilation, bool disableSdkPath)
    {
        if (_msbuildExecutable == null) return;
 
        var result = RunCommandLineCompiler(
            _msbuildExecutable,
            "/m /nr:false /t:CustomTarget Test.csproj",
            _tempDirectory,
            new Dictionary<string, string>
            {
                { "File.cs", """
                    System.Console.WriteLine("Hello from file");
                    """ },
                { "Test.csproj", $"""
                    <Project>
                        <UsingTask TaskName="Microsoft.CodeAnalysis.BuildTasks.Csc" AssemblyFile="{_buildTaskDll}" />
                        <Target Name="CustomTarget">
                            <Csc Sources="File.cs" UseSharedCompilation="{useSharedCompilation}" DisableSdkPath="{disableSdkPath}" />
                        </Target>
                    </Project>
                    """ },
            });
        _output.WriteLine(result.Output);
 
        if (disableSdkPath)
        {
            Assert.NotEqual(0, result.ExitCode);
            // Either error CS0006: Metadata file could not be found
            // or error CS0518: Predefined type is not defined or imported
            Assert.Contains("error CS", result.Output);
        }
        else
        {
            Assert.Equal(0, result.ExitCode);
            Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output);
        }
    }
 
    [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79907")]
    public void StdLib_Vbc(bool useSharedCompilation, bool disableSdkPath)
    {
        if (_msbuildExecutable == null) return;
 
        var result = RunCommandLineCompiler(
            _msbuildExecutable,
            "/m /nr:false /t:CustomTarget Test.vbproj",
            _tempDirectory,
            new Dictionary<string, string>
            {
                { "File.vb", """
                    Public Module Program
                        Public Sub Main()
                            System.Console.WriteLine("Hello from file")
                        End Sub
                    End Module
                    """ },
                { "Test.vbproj", $"""
                    <Project>
                        <UsingTask TaskName="Microsoft.CodeAnalysis.BuildTasks.Vbc" AssemblyFile="{_buildTaskDll}" />
                        <Target Name="CustomTarget">
                            <Vbc Sources="File.vb" UseSharedCompilation="{useSharedCompilation}" DisableSdkPath="{disableSdkPath}" />
                        </Target>
                    </Project>
                    """ },
            });
        _output.WriteLine(result.Output);
 
        if (disableSdkPath)
        {
            Assert.NotEqual(0, result.ExitCode);
            // error BC2017: could not find library 'Microsoft.VisualBasic.dll'
            Assert.Contains("error BC2017", result.Output);
        }
        else
        {
            Assert.Equal(0, result.ExitCode);
            Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output);
        }
    }
}