Skip to content

Commit

Permalink
Update existing and add new tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ladipro committed Apr 10, 2024
1 parent 9e39417 commit 27745c6
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 11 deletions.
208 changes: 208 additions & 0 deletions src/Build.UnitTests/BackEnd/RequestedProjectState_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using FluentAssertions;
using Microsoft.Build.Execution;
using Shouldly;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.Build.UnitTests.BackEnd
{
public class RequestedProjectState_Tests
{
[Fact]
public void DeepCloneEmpty()
{
RequestedProjectState state = new();
RequestedProjectState clone = state.DeepClone();

clone.PropertyFilters.Should().BeNull();
clone.ItemFilters.Should().BeNull();
}

[Fact]
public void DeepCloneProperties()
{
List<string> properties = ["prop1", "prop2"];
RequestedProjectState state = new()
{
PropertyFilters = properties,
};
RequestedProjectState clone = state.DeepClone();

clone.PropertyFilters.Should().BeEquivalentTo(properties);
clone.ItemFilters.Should().BeNull();

// Mutating the original instance is not reflected in the clone.
properties.Add("prop3");
clone.PropertyFilters.Count.Should().NotBe(properties.Count);
}

[Fact]
public void DeepCloneItemsNoMetadata()
{
Dictionary<string, List<string>> items = new()
{
{ "item1", null! },
{ "item2", null! },
};
RequestedProjectState state = new()
{
ItemFilters = items,
};
RequestedProjectState clone = state.DeepClone();

clone.PropertyFilters.Should().BeNull();
clone.ItemFilters.Should().BeEquivalentTo(items);

// Mutating the original instance is not reflected in the clone.
items.Add("item3", null!);
clone.ItemFilters.Count.Should().NotBe(items.Count);
}

[Fact]
public void DeepCloneItemsWithMetadata()
{
Dictionary<string, List<string>> items = new()
{
{ "item1", ["metadatum1", "metadatum2"] },
{ "item2", ["metadatum3"] },
};
RequestedProjectState state = new()
{
ItemFilters = items,
};
RequestedProjectState clone = state.DeepClone();

clone.PropertyFilters.Should().BeNull();
clone.ItemFilters.Should().BeEquivalentTo(items);

// Mutating the original instance is not reflected in the clone.
items.Add("item3", ["metadatum4"]);
clone.ItemFilters.Count.Should().NotBe(items.Count);
}

[Fact]
public void IsSubsetOfEmpty()
{
RequestedProjectState state1 = new();
RequestedProjectState state2 = new();

// Empty instances are subsets of each other.
state1.IsSubsetOf(state2).Should().BeTrue();
state2.IsSubsetOf(state1).Should().BeTrue();

state1.PropertyFilters = ["prop1"];
state1.ItemFilters = new Dictionary<string, List<string>>()
{
{ "item1", null! },
};

// Non-empty instance is a subset of empty instance but not the other way round.
state1.IsSubsetOf(state2).Should().BeTrue();
state2.IsSubsetOf(state1).Should().BeFalse();
}

[Fact]
public void IsSubsetOfProperties()
{
RequestedProjectState state1 = new()
{
PropertyFilters = ["prop1"],
};
RequestedProjectState state2 = new()
{
PropertyFilters = ["prop1", "prop2"],
};

// "prop1" is a subset of "prop1", "prop2".
state1.IsSubsetOf(state2).Should().BeTrue();
state2.IsSubsetOf(state1).Should().BeFalse();

state1.PropertyFilters.Add("prop3");

// Disjoint sets are not subsets of each other.
state1.IsSubsetOf(state2).Should().BeFalse();
state2.IsSubsetOf(state1).Should().BeFalse();

state1.PropertyFilters.Clear();

// Empty props is a subset of anything.
state1.IsSubsetOf(state2).Should().BeTrue();
state2.IsSubsetOf(state1).Should().BeFalse();
}

[Fact]
public void IsSubsetOfItemsNoMetadata()
{
RequestedProjectState state1 = new()
{
ItemFilters = new Dictionary<string, List<string>>()
{
{ "item1", null! },
},
};
RequestedProjectState state2 = new()
{
ItemFilters = new Dictionary<string, List<string>>()
{
{ "item1", null! },
{ "item2", null! },
},
};

// "item1" is a subset of "item1", "item2".
state1.IsSubsetOf(state2).Should().BeTrue();
state2.IsSubsetOf(state1).Should().BeFalse();

state1.ItemFilters.Add("item3", null!);

// Disjoint sets are not subsets of each other.
state1.IsSubsetOf(state2).Should().BeFalse();
state2.IsSubsetOf(state1).Should().BeFalse();

state1.ItemFilters.Clear();

// Empty items is a subset of anything.
state1.IsSubsetOf(state2).Should().BeTrue();
state2.IsSubsetOf(state1).Should().BeFalse();
}

[Fact]
public void IsSubsetOfItemsWithMetadata()
{
RequestedProjectState state1 = new()
{
ItemFilters = new Dictionary<string, List<string>>()
{
{ "item1", ["metadatum1"] },
},
};
RequestedProjectState state2 = new()
{
ItemFilters = new Dictionary<string, List<string>>()
{
{ "item1", null! },
},
};

// "item1" with "metadatum1" is a subset of "item1" with no metadata filter.
state1.IsSubsetOf(state2).Should().BeTrue();
state2.IsSubsetOf(state1).Should().BeFalse();

state2.ItemFilters["item1"] = ["metadatum2"];

// Disjoint metadata filters are not subsets of each other.
state1.IsSubsetOf(state2).Should().BeFalse();
state2.IsSubsetOf(state1).Should().BeFalse();

state1.ItemFilters["item1"] = [];

// Empty metadata filter is a subset of any other metadata filter.
state1.IsSubsetOf(state2).Should().BeTrue();
state2.IsSubsetOf(state1).Should().BeFalse();
}
}
}
50 changes: 39 additions & 11 deletions src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Internal;
Expand Down Expand Up @@ -265,6 +269,10 @@ public void TestCacheOnDifferentBuildFlagsPerRequest_ProvideSubsetOfStateAfterBu
int nodeRequestId = 0;
int configurationId = 1;

RequestedProjectState requestedProjectState1 = new()
{
PropertyFilters = ["property1", "property2"],
};
BuildRequest requestWithSubsetFlag1 = new BuildRequest(
submissionId,
nodeRequestId,
Expand All @@ -273,8 +281,13 @@ public void TestCacheOnDifferentBuildFlagsPerRequest_ProvideSubsetOfStateAfterBu
null /* hostServices */,
BuildEventContext.Invalid /* parentBuildEventContext */,
null /* parentRequest */,
BuildRequestDataFlags.ProvideSubsetOfStateAfterBuild);
BuildRequestDataFlags.ProvideSubsetOfStateAfterBuild,
requestedProjectState1);

RequestedProjectState requestedProjectState2 = new()
{
PropertyFilters = ["property1"],
};
BuildRequest requestWithSubsetFlag2 = new BuildRequest(
submissionId,
nodeRequestId,
Expand All @@ -283,30 +296,45 @@ public void TestCacheOnDifferentBuildFlagsPerRequest_ProvideSubsetOfStateAfterBu
null /* hostServices */,
BuildEventContext.Invalid /* parentBuildEventContext */,
null /* parentRequest */,
BuildRequestDataFlags.ProvideSubsetOfStateAfterBuild);
BuildRequestDataFlags.ProvideSubsetOfStateAfterBuild,
requestedProjectState2);

BuildResult resultForRequestWithSubsetFlag1 = new(requestWithSubsetFlag1);
resultForRequestWithSubsetFlag1.AddResultsForTarget(targetName, BuildResultUtilities.GetEmptySucceedingTargetResult());

using TextReader textReader = new StringReader(@"
<Project>
<PropertyGroup>
<property1>Value1</property1>
<property2>Value2</property2>
</PropertyGroup>
</Project>
");
using XmlReader xmlReader = XmlReader.Create(textReader);
resultForRequestWithSubsetFlag1.ProjectStateAfterBuild = new ProjectInstance(ProjectRootElement.Create(xmlReader)).FilteredCopy(requestedProjectState1);

ResultsCache cache = new();
cache.AddResult(resultForRequestWithSubsetFlag1);

ResultsCacheResponse cachedResponseWithSubsetFlag1 = cache.SatisfyRequest(
requestWithSubsetFlag1,
new List<string>(),
new List<string>(new string[] { targetName }),
skippedResultsDoNotCauseCacheMiss: false);
requestWithSubsetFlag1,
new List<string>(),
new List<string>(new string[] { targetName }),
skippedResultsDoNotCauseCacheMiss: false);

ResultsCacheResponse cachedResponseWithSubsetFlag2 = cache.SatisfyRequest(
requestWithSubsetFlag2,
new List<string>(),
new List<string>(new string[] { targetName }),
skippedResultsDoNotCauseCacheMiss: false);

// It was agreed not to return cache results if ProvideSubsetOfStateAfterBuild is passed,
// because RequestedProjectState may have different user filters defined.
// It is more reliable to ignore the cached value.
Assert.Equal(ResultsCacheResponseType.NotSatisfied, cachedResponseWithSubsetFlag1.Type);
Assert.Equal(ResultsCacheResponseType.NotSatisfied, cachedResponseWithSubsetFlag2.Type);
Assert.Equal(ResultsCacheResponseType.Satisfied, cachedResponseWithSubsetFlag1.Type);
Assert.Equal("Value1", cachedResponseWithSubsetFlag1.Results.ProjectStateAfterBuild.GetPropertyValue("property1"));
Assert.Equal("Value2", cachedResponseWithSubsetFlag1.Results.ProjectStateAfterBuild.GetPropertyValue("property2"));

Assert.Equal(ResultsCacheResponseType.Satisfied, cachedResponseWithSubsetFlag2.Type);
Assert.Equal("Value1", cachedResponseWithSubsetFlag2.Results.ProjectStateAfterBuild.GetPropertyValue("property1"));
Assert.Equal("", cachedResponseWithSubsetFlag2.Results.ProjectStateAfterBuild.GetPropertyValue("property2"));
}

[Fact]
Expand Down

0 comments on commit 27745c6

Please sign in to comment.