File: TestAssemblyInfo.cs
Web Access
Project: ..\..\..\src\Utilities.UnitTests\Microsoft.Build.Utilities.UnitTests.csproj (Microsoft.Build.Utilities.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 System.Runtime.InteropServices;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Build.Shared;
using Microsoft.Build.Shared.FileSystem;
using Microsoft.Build.UnitTests;
using Xunit;
 
#nullable disable
 
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
 
// Register test framework for assembly fixture
[assembly: TestFramework("Xunit.NetCore.Extensions.XunitTestFrameworkWithAssemblyFixture", "Xunit.NetCore.Extensions")]
 
[assembly: AssemblyFixture(typeof(MSBuildTestAssemblyFixture))]
 
// Wrap a TestEnvironment around each test method and class so if invariants have changed we will know where
[assembly: AssemblyFixture(typeof(MSBuildTestEnvironmentFixture), LifetimeScope = AssemblyFixtureAttribute.Scope.Class)]
[assembly: AssemblyFixture(typeof(MSBuildTestEnvironmentFixture), LifetimeScope = AssemblyFixtureAttribute.Scope.Method)]
 
namespace Microsoft.Build.UnitTests
{
    public class MSBuildTestAssemblyFixture : IDisposable
    {
        private bool _disposed;
        private TestEnvironment _testEnvironment;
 
        public MSBuildTestAssemblyFixture()
        {
            // Set field to indicate tests are running in the TestInfo class in Microsoft.Build.Framework.
            //  See the comments on the TestInfo class for an explanation of why it works this way.
            var frameworkAssembly = typeof(Microsoft.Build.Framework.ITask).Assembly;
            var testInfoType = frameworkAssembly.GetType("Microsoft.Build.Framework.TestInfo");
            var runningTestsField = testInfoType.GetField("s_runningTests", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            runningTestsField.SetValue(null, true);
 
            // Set the field in BuildEnvironmentState - as it might have been already preintialized by the data preparation of data driven tests
            testInfoType = frameworkAssembly.GetType("Microsoft.Build.Framework.BuildEnvironmentState");
            runningTestsField = testInfoType.GetField("s_runningTests", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            runningTestsField.SetValue(null, true);
 
 
            // Note: build error files will be initialized in test environments for particular tests, also we don't have output to report error files into anyway...
            _testEnvironment = TestEnvironment.Create(output: null, ignoreBuildErrorFiles: true);
 
            _testEnvironment.DoNotLaunchDebugger();
 
            // Reset the VisualStudioVersion environment variable.  This will be set if tests are run from a VS command prompt.  However,
            //  if the environment variable is set, it will interfere with tests which set the SubToolsetVersion
            //  (VerifySubToolsetVersionSetByConstructorOverridable), as the environment variable would take precedence.
            _testEnvironment.SetEnvironmentVariable("VisualStudioVersion", null);
 
            // Prevent test assemblies from logging any performance info.
            // https://github.com/dotnet/msbuild/pull/6274
            _testEnvironment.SetEnvironmentVariable("DOTNET_PERFLOG_DIR", null);
 
            SetDotnetHostPath(_testEnvironment);
 
            // Use a project-specific temporary path
            //  This is so multiple test projects can be run in parallel without sharing the same temp directory
            var subdirectory = Path.GetRandomFileName();
 
            string newTempPath = Path.Combine(Path.GetTempPath(), subdirectory);
            var assemblyTempFolder = _testEnvironment.CreateFolder(newTempPath);
 
            _testEnvironment.SetTempPath(assemblyTempFolder.Path);
 
            // Lets clear FileUtilities.TempFileDirectory in case it was already initialized by other code, so it picks up new TempPath
            FileUtilities.ClearTempFileDirectory();
 
            _testEnvironment.CreateFile(
                transientTestFolder: assemblyTempFolder,
                fileName: "MSBuild_Tests.txt",
                contents: $"Temporary test folder for tests from {AppContext.BaseDirectory}");
 
            // Ensure that we stop looking for a D.B.rsp at the root of the test temp
            _testEnvironment.CreateFile(
                transientTestFolder: assemblyTempFolder,
                fileName: "Directory.Build.rsp",
                contents: string.Empty);
 
            _testEnvironment.CreateFile(
                transientTestFolder: assemblyTempFolder,
                fileName: "Directory.Build.props",
                contents: "<Project />");
 
            _testEnvironment.CreateFile(
                transientTestFolder: assemblyTempFolder,
                fileName: "Directory.Build.targets",
                contents: "<Project />");
        }
 
        /// <summary>
        /// Find correct version of "dotnet", and set DOTNET_HOST_PATH so that the Roslyn tasks will use the right host
        /// </summary>
        /// <param name="testEnvironment"></param>
        private static void SetDotnetHostPath(TestEnvironment testEnvironment)
        {
            var currentFolder = AppContext.BaseDirectory;
 
            while (currentFolder != null)
            {
                string potentialVersionsPropsPath = Path.Combine(currentFolder, "build", "Versions.props");
                if (FileSystems.Default.FileExists(potentialVersionsPropsPath))
                {
                    XDocument doc = null;
                    var xrs = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, CloseInput = true, IgnoreWhitespace = true };
                    using (XmlReader xr = XmlReader.Create(File.OpenRead(potentialVersionsPropsPath), xrs))
                    {
                        doc = XDocument.Load(xr);
                    }
 
                    var ns = doc.Root.Name.Namespace;
                    var cliVersionElement = doc.Root.Elements(ns + "PropertyGroup").Elements(ns + "DotNetCliVersion").FirstOrDefault();
                    if (cliVersionElement != null)
                    {
                        string cliVersion = cliVersionElement.Value;
                        string dotnetPath = Path.Combine(currentFolder, "artifacts", ".dotnet", cliVersion, "dotnet");
 
                        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                        {
                            dotnetPath += ".exe";
                        }
 
                        testEnvironment.SetEnvironmentVariable("DOTNET_HOST_PATH", dotnetPath);
                    }
 
                    break;
                }
 
                currentFolder = Directory.GetParent(currentFolder)?.FullName;
            }
        }
 
        public void Dispose()
        {
            if (!_disposed)
            {
                _testEnvironment.Dispose();
 
                _disposed = true;
            }
        }
    }
 
    public class MSBuildTestEnvironmentFixture : IDisposable
    {
        private bool _disposed;
        private TestEnvironment _testEnvironment;
 
        public MSBuildTestEnvironmentFixture()
        {
            _testEnvironment = TestEnvironment.Create();
        }
 
        public void Dispose()
        {
            if (!_disposed)
            {
                _testEnvironment.Dispose();
 
                _disposed = true;
            }
        }
    }
}