Skip to content

Commit

Permalink
Make AnalyzerResults deterministically ordered
Browse files Browse the repository at this point in the history
The order itself is not relevant (the `NuGetFrameworkSorter` used to sort target frameworks doesn't guarantee any specific order anyway) but the order is deterministic, meaning that several builds of the same project will always yield the same order of the target frameworks and results.

The nondeterministic nature of the current implementation made it hard to diagnose an implementation issue in Stryker.NET: stryker-mutator/stryker-net#1899 (comment)
  • Loading branch information
0xced authored and daveaglick committed Feb 14, 2022
1 parent 82e9c60 commit 524bfee
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 6 deletions.
7 changes: 4 additions & 3 deletions src/Buildalyzer/AnalyzerResults.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Buildalyzer
{
Expand All @@ -23,17 +24,17 @@ internal void Add(IEnumerable<IAnalyzerResult> results, bool overallSuccess)

public IAnalyzerResult this[string targetFramework] => _results[targetFramework];

public IEnumerable<string> TargetFrameworks => _results.Keys;
public IEnumerable<string> TargetFrameworks => _results.Keys.OrderBy(e => e, TargetFrameworkComparer.Instance);

public IEnumerable<IAnalyzerResult> Results => _results.Values;
public IEnumerable<IAnalyzerResult> Results => TargetFrameworks.Select(e => _results[e]);

public int Count => _results.Count;

public bool ContainsTargetFramework(string targetFramework) => _results.ContainsKey(targetFramework);

public bool TryGetTargetFramework(string targetFramework, out IAnalyzerResult result) => _results.TryGetValue(targetFramework, out result);

public IEnumerator<IAnalyzerResult> GetEnumerator() => _results.Values.GetEnumerator();
public IEnumerator<IAnalyzerResult> GetEnumerator() => Results.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Expand Down
3 changes: 2 additions & 1 deletion src/Buildalyzer/Buildalyzer.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
<Description>A little utility to perform design-time builds of .NET projects without having to think too hard about it. Should work with any project type on any .NET runtime.</Description>
Expand All @@ -8,6 +8,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="MsBuildPipeLogger.Server" Version="1.1.3" />
<PackageReference Include="NuGet.Frameworks" Version="6.0.0" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />
<PackageReference Include="Microsoft.Build" Version="16.9.0" />
<PackageReference Include="Microsoft.Build.Framework" Version="16.9.0" />
Expand Down
23 changes: 23 additions & 0 deletions src/Buildalyzer/TargetFrameworkComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Generic;
using NuGet.Frameworks;

namespace Buildalyzer
{
internal class TargetFrameworkComparer : IComparer<string>
{
public static readonly TargetFrameworkComparer Instance = new TargetFrameworkComparer();

private static readonly NuGetFrameworkSorter Sorter = new NuGetFrameworkSorter();

private TargetFrameworkComparer()
{
}

public int Compare(string x, string y)
{
NuGetFramework xFramework = NuGetFramework.ParseFolder(x);
NuGetFramework yFramework = NuGetFramework.ParseFolder(y);
return Sorter.Compare(xFramework, yFramework);
}
}
}
4 changes: 2 additions & 2 deletions tests/Buildalyzer.Tests/Integration/SimpleProjectsFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
Expand Down Expand Up @@ -236,7 +236,7 @@ public void MultiTargetingBuildAllTargetFrameworksGetsSourceFiles()
// Then
// Multi-targeting projects product an extra result with an empty target framework that holds some MSBuild properties (I.e. the "outer" build)
results.Count.ShouldBe(3);
results.TargetFrameworks.ShouldBe(new[] { "net462", "netstandard2.0", string.Empty }, true, log.ToString());
results.TargetFrameworks.ShouldBe(new[] { "net462", "netstandard2.0", string.Empty }, ignoreOrder: false, log.ToString());
results[string.Empty].SourceFiles.ShouldBeEmpty();
new[]
{
Expand Down

0 comments on commit 524bfee

Please sign in to comment.