File: SdkIntegrationTests.cs
Web Access
Project: src\src\Compilers\Core\MSBuildTaskTests\Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj (Microsoft.Build.Tasks.CodeAnalysis.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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
using Basic.CompilerLog.Util;
 
namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests;
 
public sealed class SdkIntegrationTests : IDisposable
{
    public const string NetCoreTfm = "net9.0";
 
    public ITestOutputHelper TestOutputHelper { get; }
    public TempRoot Temp { get; }
    public TempDirectory ProjectDir { get; }
    public ArtifactUploadUtil ArtifactUploadUtil { get; }
 
    public SdkIntegrationTests(ITestOutputHelper testOutputHelper)
    {
        Assert.NotNull(DotNetSdkTestBase.DotNetInstallDir);
        TestOutputHelper = testOutputHelper;
        Temp = new TempRoot();
        ProjectDir = Temp.CreateDirectory();
        ArtifactUploadUtil = new ArtifactUploadUtil(testOutputHelper);
    }
 
    public void Dispose()
    {
        Temp.Dispose();
        ArtifactUploadUtil.Dispose();
    }
 
    public static string GetRoslynTargetsPath()
    {
        var p = typeof(SdkIntegrationTests).Assembly.Location!;
        var dir = Path.GetDirectoryName(p)!;
        var targets = Path.Combine(dir, "Microsoft.CSharp.Core.targets");
        Assert.True(File.Exists(targets));
        return dir;
    }
 
    /// <summary>
    /// Runs the build and returns the path to the binary log file
    /// </summary>
    private string RunBuild(
        string projectFilePath,
        string? additionalArguments = null,
        IEnumerable<KeyValuePair<string, string>>? additionalEnvironmentVars = null,
        bool succeeds = true)
    {
        var workingDirectory = Path.GetDirectoryName(projectFilePath)!;
        ArtifactUploadUtil.AddDirectory(workingDirectory);
 
        var args = new StringBuilder();
        args.Append("build /bl ");
        args.Append($"/p:RoslynTargetsPath={GetRoslynTargetsPath()} ");
        args.Append($"/p:RoslynTasksAssembly={typeof(Csc).Assembly.Location} ");
        args.Append($"/p:RoslynCompilerType=Custom ");
        if (additionalArguments is not null)
        {
            args.Append(additionalArguments);
        }
 
        var result = ProcessUtilities.Run(DotNetSdkTestBase.DotNetExeName, args.ToString(), workingDirectory, additionalEnvironmentVars);
        if (succeeds)
        {
            Assert.True(result.ExitCode == 0, $"MSBuild failed with exit code {result.ExitCode}: {result.Output}");
        }
        else
        {
            Assert.False(result.ExitCode == 0, $"MSBuild failed with exit code {result.ExitCode}: {result.Output}");
        }
 
        return Path.Combine(workingDirectory, "msbuild.binlog");
    }
 
    private static List<Compilation> ReadCompilations(string binaryLogPath)
    {
        using var reader = BinaryLogReader.Create(binaryLogPath, BasicAnalyzerKind.None);
        var list = new List<Compilation>();
        foreach (var compilerCall in reader.ReadAllCompilerCalls())
        {
            var compilation = reader.ReadCompilationData(compilerCall).GetCompilationAfterGenerators();
            list.Add(compilation);
        }
 
        return list;
    }
 
    [ConditionalFact(typeof(DotNetSdkAvailable))]
    public void Console()
    {
        var projectFile = ProjectDir.CreateFile("console.csproj");
        projectFile.WriteAllText($"""
            <Project Sdk="Microsoft.NET.Sdk">
 
              <PropertyGroup>
                <OutputType>Exe</OutputType>
                <TargetFramework>{NetCoreTfm}</TargetFramework>
                <ImplicitUsings>enable</ImplicitUsings>
                <Nullable>enable</Nullable>
              </PropertyGroup>
            </Project>
            """);
 
        ProjectDir.CreateFile("hello.cs").WriteAllText("""
            Console.WriteLine("Hello, World!");
            """);
 
        var binlogPath = RunBuild(projectFile.Path);
        var compilations = ReadCompilations(binlogPath);
        Assert.Single(compilations);
        Assert.True(compilations[0].SyntaxTrees.Any(x => Path.GetFileName(x.FilePath) == "hello.cs"));
        ArtifactUploadUtil.SetSucceeded();
    }
 
    [ConditionalTheory(typeof(DotNetSdkAvailable))]
    [InlineData(NetCoreTfm, true)]
    [InlineData("net6.0", true)]
    [InlineData("netstandard2.0", false)]
    [InlineData("net472", false)]
    public void StrongNameWarningCSharp(string tfm, bool expectStrongNameSuppression)
    {
        var projectFile = ProjectDir.CreateFile("console.csproj");
        projectFile.WriteAllText($"""
            <Project Sdk="Microsoft.NET.Sdk">
 
              <PropertyGroup>
                <OutputType>Library</OutputType>
                <TargetFramework>{tfm}</TargetFramework>
              </PropertyGroup>
            </Project>
            """);
 
        ProjectDir.CreateFile("hello.cs").WriteAllText("""
            class C { }
            """);
 
        var binlogPath = RunBuild(projectFile.Path);
        var compilation = ReadCompilations(binlogPath).Single();
        var options = compilation.Options;
        if (expectStrongNameSuppression)
        {
            Assert.True(options.SpecificDiagnosticOptions.TryGetValue("CS8002", out ReportDiagnostic d));
            Assert.Equal(ReportDiagnostic.Suppress, d);
        }
        else
        {
            Assert.False(options.SpecificDiagnosticOptions.TryGetValue("CS8002", out _));
        }
 
        ArtifactUploadUtil.SetSucceeded();
    }
 
    [ConditionalTheory(typeof(DotNetSdkAvailable))]
    [InlineData(NetCoreTfm, true)]
    [InlineData("net6.0", true)]
    [InlineData("netstandard2.0", false)]
    [InlineData("net472", false)]
    public void StrongNameWarningVisualBasic(string tfm, bool expectStrongNameSuppression)
    {
        var projectFile = ProjectDir.CreateFile("console.vbproj");
        projectFile.WriteAllText($"""
            <Project Sdk="Microsoft.NET.Sdk">
 
              <PropertyGroup>
                <OutputType>Library</OutputType>
                <TargetFramework>{tfm}</TargetFramework>
              </PropertyGroup>
            </Project>
            """);
 
        ProjectDir.CreateFile("hello.vb").WriteAllText("""
            Module M
            End Module
            """);
 
        var binlogPath = RunBuild(projectFile.Path);
        var compilation = ReadCompilations(binlogPath).Single();
        var options = compilation.Options;
        if (expectStrongNameSuppression)
        {
            Assert.True(options.SpecificDiagnosticOptions.TryGetValue("BC41997", out ReportDiagnostic d));
            Assert.Equal(ReportDiagnostic.Suppress, d);
        }
        else
        {
            Assert.False(options.SpecificDiagnosticOptions.TryGetValue("BC41997", out _));
        }
 
        ArtifactUploadUtil.SetSucceeded();
    }
}