File: OutputPathTests.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 Microsoft.Build.Evaluation;
using Microsoft.Build.Shared;
using Microsoft.Build.UnitTests;
 
using Shouldly;
 
using Xunit;
using Xunit.Abstractions;
using Xunit.NetCore.Extensions;
 
#nullable disable
 
namespace Microsoft.Build.Tasks.UnitTests
{
    public sealed class OutputPathTests : IDisposable
    {
        private readonly ITestOutputHelper _output;
        private readonly string _projectRelativePath = Path.Combine("src", "test", "test.csproj");
 
        public OutputPathTests(ITestOutputHelper output)
        {
            _output = output;
            ObjectModelHelpers.DeleteTempProjectDirectory();
        }
 
        public void Dispose()
        {
            ObjectModelHelpers.DeleteTempProjectDirectory();
        }
 
        /// <summary>
        /// Test when both BaseOutputPath and OutputPath are not specified.
        /// </summary>
        [Fact]
        public void BothBaseOutputPathAndOutputPathWereNotSpecified()
        {
            // Arrange
            var baseOutputPath = "bin";
 
            var projectFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(_projectRelativePath,
$@"<Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`>
 
    <Import Project=`$(MSBuildToolsPath)\Microsoft.Common.props`/>
 
    <PropertyGroup>
        <Platform>AnyCPU</Platform>
        <Configuration>Debug</Configuration>
    </PropertyGroup>
 
    <Import Project=`$(MSBuildToolsPath)\Microsoft.Common.targets`/>
    <Target Name=`Build`/>
 
</Project>");
 
            // Act
            Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory(projectFilePath, touchProject: false);
 
            project.Build(new MockLogger(_output)).ShouldBeFalse();
 
            // Assert
            project.GetPropertyValue("BaseOutputPath").ShouldBe(baseOutputPath + '\\');
            project.GetPropertyValue("BaseOutputPathWasSpecified").ShouldBe(string.Empty);
            project.GetPropertyValue("_OutputPathWasMissing").ShouldBe("true");
        }
 
        /// <summary>
        /// Test when BaseOutputPath is specified without the OutputPath.
        /// </summary>
        [Fact]
        public void BaseOutputPathWasSpecifiedAndIsOverridable()
        {
            // Arrange
            var baseOutputPath = Path.Combine("build", "bin");
 
            var projectFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(_projectRelativePath,
$@"<Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`>
 
    <Import Project=`$(MSBuildToolsPath)\Microsoft.Common.props`/>
 
    <PropertyGroup>
        <Platform>AnyCPU</Platform>
        <Configuration>Debug</Configuration>
        <BaseOutputPath>{baseOutputPath}</BaseOutputPath>
    </PropertyGroup>
 
    <Import Project=`$(MSBuildToolsPath)\Microsoft.Common.targets`/>
    <Target Name=`Build`/>
 
</Project>");
 
            // Act
            Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory(projectFilePath, touchProject: false);
 
            project.Build(new MockLogger(_output)).ShouldBeTrue();
 
            // Assert
            project.GetPropertyValue("BaseOutputPath").ShouldBe(baseOutputPath.WithTrailingSlash());
            project.GetPropertyValue("BaseOutputPathWasSpecified").ShouldBe("true");
            project.GetPropertyValue("_OutputPathWasMissing").ShouldBe("true");
        }
 
        /// <summary>
        /// Test when both BaseOutputPath and OutputPath are specified.
        /// </summary>
        [Fact]
        public void BothBaseOutputPathAndOutputPathWereSpecified()
        {
            // Arrange
            var baseOutputPath = Path.Combine("build", "bin");
            var outputPath = Path.Combine("bin", "Debug");
            var outputPathAlt = Path.Combine("bin", "Release");
 
            var projectFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(_projectRelativePath,
$@"<Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`>
 
    <Import Project=`$(MSBuildToolsPath)\Microsoft.Common.props`/>
 
    <PropertyGroup>
        <Platform>AnyCPU</Platform>
        <Configuration>Debug</Configuration>
    </PropertyGroup>
 
    <PropertyGroup>
        <BaseOutputPath>{baseOutputPath}</BaseOutputPath>
        <OutputPath Condition=`'$(Platform)|$(Configuration)' == 'AnyCPU|Debug'`>{outputPath}</OutputPath>
        <OutputPath Condition=`'$(Platform)|$(Configuration)' == 'AnyCPU|Release'`>{outputPathAlt}</OutputPath>
    </PropertyGroup>
 
    <Import Project=`$(MSBuildToolsPath)\Microsoft.Common.targets`/>
    <Target Name=`Build`/>
 
</Project>");
 
            // Act
            Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory(projectFilePath, touchProject: false);
 
            project.Build(new MockLogger(_output)).ShouldBeTrue();
 
            // Assert
            project.GetPropertyValue("BaseOutputPath").ShouldBe(baseOutputPath.WithTrailingSlash());
            project.GetPropertyValue("OutputPath").ShouldBe(outputPath.WithTrailingSlash());
            project.GetPropertyValue("BaseOutputPathWasSpecified").ShouldBe("true");
            project.GetPropertyValue("_OutputPathWasMissing").ShouldBe(string.Empty);
        }
 
        /// <summary>
        /// Test for [MSBuild]::NormalizePath and [MSBuild]::NormalizeDirectory returning current directory instead of current Project directory.
        /// </summary>
        [WindowsOnlyFact(Skip = "Skipping this test for now until we have a consensus about this issue.")]
        public void MSBuildNormalizePathShouldReturnProjectDirectory()
        {
            // Arrange
            var configuration = "Debug";
            var baseOutputPath = "bin";
 
            var projectFilePath = ObjectModelHelpers.CreateFileInTempProjectDirectory(_projectRelativePath,
$@"<Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`>
 
    <Import Project=`$(MSBuildToolsPath)\Microsoft.Common.props`/>
 
    <PropertyGroup Condition=`'$(OutputPath)' == ''`>
        <OutputPath>$([MSBuild]::NormalizeDirectory('{baseOutputPath}', '{configuration}'))</OutputPath>
    </PropertyGroup>
 
    <Import Project=`$(MSBuildToolsPath)\Microsoft.Common.targets`/>
    <Target Name=`Build`/>
 
</Project>");
 
            // Act
            Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory(projectFilePath, touchProject: false);
 
            project.Build(new MockLogger(_output)).ShouldBeTrue();
 
            // Assert
            project.GetPropertyValue("Configuration").ShouldBe(configuration);
            project.GetPropertyValue("BaseOutputPath").ShouldBe(baseOutputPath.WithTrailingSlash());
 
            var expectedOutputPath = FileUtilities.CombinePaths(project.DirectoryPath, baseOutputPath, configuration).WithTrailingSlash();
            project.GetPropertyValue("OutputPath").ShouldBe(expectedOutputPath);
        }
    }
}