|
// 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 Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.UnitTests;
using Microsoft.Build.Utilities;
using Shouldly;
using Xunit;
using Xunit.Abstractions;
#nullable disable
namespace Microsoft.Build.Engine.UnitTests
{
public sealed class ChangeWaves_Tests
{
private ITestOutputHelper _output;
public ChangeWaves_Tests(ITestOutputHelper output)
{
_output = output;
}
/// <summary>
/// Performs necessary operations for setting the MSBUILDDISABLEFEATURESFROMVERSION environment variable.
/// This is required because Change Waves is static and stale values can be seen between tests in the same assembly.
/// </summary>
/// <param name="wave">The version to set as the current Change Wave.</param>
private void SetChangeWave(string wave, TestEnvironment env)
{
ChangeWaves.ResetStateForTests();
env.SetEnvironmentVariable("MSBUILDDISABLEFEATURESFROMVERSION", wave);
BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly();
}
/// <summary>
/// Helper function to build a simple project based on a particular change wave being set.
/// Call SetChangeWave on your TestEnvironment before calling this function.
/// </summary>
/// <param name="testEnvironment">The TestEnvironment being used for this test.</param>
/// <param name="versionToCheckAgainstCurrentChangeWave">The version to compare to the current set Change Wave.</param>
/// <param name="currentChangeWaveShouldUltimatelyResolveTo">What the project property for the environment variable MSBUILDDISABLEFEATURESFROMVERSION ultimately resolves to.</param>
/// <param name="warningCodesLogShouldContain">An array of warning codes that should exist in the resulting log. Ex: "MSB4271".</param>
private void buildSimpleProjectAndValidateChangeWave(TestEnvironment testEnvironment, Version versionToCheckAgainstCurrentChangeWave, Version currentChangeWaveShouldUltimatelyResolveTo, params string[] warningCodesLogShouldContain)
{
bool isThisWaveEnabled = versionToCheckAgainstCurrentChangeWave < currentChangeWaveShouldUltimatelyResolveTo || currentChangeWaveShouldUltimatelyResolveTo == ChangeWaves.EnableAllFeatures;
ChangeWaves.AreFeaturesEnabled(versionToCheckAgainstCurrentChangeWave).ShouldBe(isThisWaveEnabled);
string projectFile = $"" +
$"<Project>" +
$"<Target Name='HelloWorld' Condition=\"$([MSBuild]::AreFeaturesEnabled('{versionToCheckAgainstCurrentChangeWave}')) and '$(MSBUILDDISABLEFEATURESFROMVERSION)' == '{currentChangeWaveShouldUltimatelyResolveTo}'\">" +
$"<Message Text='Hello World!'/>" +
$"</Target>" +
$"</Project>";
TransientTestFile file = testEnvironment.CreateFile("proj.csproj", projectFile);
using ProjectCollection collection = new ProjectCollection();
MockLogger log = new MockLogger(_output);
collection.RegisterLogger(log);
Project p = collection.LoadProject(file.Path);
p.Build().ShouldBeTrue();
log.FullLog.Contains("Hello World!").ShouldBe(isThisWaveEnabled);
if (warningCodesLogShouldContain != null)
{
log.WarningCount.ShouldBe(warningCodesLogShouldContain.Length);
log.AssertLogContains(warningCodesLogShouldContain);
}
ChangeWaves.ResetStateForTests();
}
[Fact]
public void EnableAllFeaturesBehindChangeWavesEnablesAllFeaturesBehindChangeWaves()
{
using (TestEnvironment env = TestEnvironment.Create())
{
SetChangeWave(ChangeWaves.EnableAllFeatures.ToString(), env);
for (int i = 0; i < ChangeWaves.AllWaves.Length - 1; i++)
{
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: ChangeWaves.AllWaves[i],
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.EnableAllFeatures,
warningCodesLogShouldContain: null);
}
}
}
[Theory]
[InlineData("16.8")]
[InlineData("16.10")]
[InlineData("17.0")]
[InlineData("27.3")]
public void NoChangeWaveSetMeansAllChangeWavesAreEnabled(string featureVersion)
{
using (TestEnvironment env = TestEnvironment.Create())
{
// Reset static ChangeWave
SetChangeWave(string.Empty, env);
Version featureAsVersion = Version.Parse(featureVersion);
ChangeWaves.AreFeaturesEnabled(featureAsVersion).ShouldBe(true);
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: featureAsVersion,
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.EnableAllFeatures,
warningCodesLogShouldContain: null);
}
}
[Theory]
[InlineData("test")]
[InlineData("16_8")]
[InlineData("16x8")]
[InlineData("garbage")]
public void InvalidFormatThrowsWarningAndLeavesFeaturesEnabled(string disableFeaturesFromVersion)
{
using (TestEnvironment env = TestEnvironment.Create())
{
SetChangeWave(disableFeaturesFromVersion, env);
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: ChangeWaves.HighestWave,
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.EnableAllFeatures,
warningCodesLogShouldContain: "MSB4271");
}
}
[Theory]
[InlineData("0.8")]
[InlineData("4.5")]
[InlineData("10.0")]
public void VersionTooLowClampsToLowestVersionInRotation(string disableFeaturesFromVersion)
{
using (TestEnvironment env = TestEnvironment.Create())
{
SetChangeWave(disableFeaturesFromVersion, env);
// All waves should be disabled
for (int i = 0; i < ChangeWaves.AllWaves.Length; i++)
{
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: ChangeWaves.AllWaves[i],
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.LowestWave,
warningCodesLogShouldContain: "MSB4272");
}
}
}
[Theory]
[InlineData("100.10")]
[InlineData("203.45")]
public void VersionTooHighClampsToHighestVersionInRotation(string disableFeaturesFromVersion)
{
using (TestEnvironment env = TestEnvironment.Create())
{
SetChangeWave(disableFeaturesFromVersion, env);
// all waves but the highest should pass
for (int i = 0; i < ChangeWaves.AllWaves.Length - 1; i++)
{
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: ChangeWaves.AllWaves[i],
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.HighestWave,
warningCodesLogShouldContain: "MSB4272");
}
// Make sure the last wave is disabled.
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: ChangeWaves.AllWaves[ChangeWaves.AllWaves.Length - 1],
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.HighestWave,
warningCodesLogShouldContain: "MSB4272");
}
}
[Fact]
public void VersionSetToValidValueButInvalidVersionSetsNextVersion()
{
using (TestEnvironment env = TestEnvironment.Create())
{
SetChangeWave($"{ChangeWaves.LowestWave.Major}.{ChangeWaves.LowestWave.Minor}.{ChangeWaves.LowestWave.Build + 1}", env);
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: ChangeWaves.LowestWave,
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.AllWaves[1],
warningCodesLogShouldContain: null);
}
}
[Fact]
public void CorrectlyDetermineEnabledFeatures()
{
using (TestEnvironment env = TestEnvironment.Create())
{
SetChangeWave(ChangeWaves.HighestWave.ToString(), env);
for (int i = 0; i < ChangeWaves.AllWaves.Length - 1; i++)
{
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: ChangeWaves.AllWaves[i],
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.HighestWave,
warningCodesLogShouldContain: null);
}
// Make sure the last wave is disabled.
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: ChangeWaves.AllWaves[ChangeWaves.AllWaves.Length - 1],
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.HighestWave,
warningCodesLogShouldContain: null);
}
}
[Fact]
public void CorrectlyDetermineDisabledFeatures()
{
using (TestEnvironment env = TestEnvironment.Create())
{
SetChangeWave(ChangeWaves.LowestWave.ToString(), env);
foreach (Version wave in ChangeWaves.AllWaves)
{
buildSimpleProjectAndValidateChangeWave(testEnvironment: env,
versionToCheckAgainstCurrentChangeWave: wave,
currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.LowestWave,
warningCodesLogShouldContain: null);
}
}
}
}
}
|