Skip to content

Commit

Permalink
do in-place dependency discovery via MSBuild binlog
Browse files Browse the repository at this point in the history
  • Loading branch information
brettfo committed Sep 17, 2024
1 parent 93e1827 commit 8fda407
Show file tree
Hide file tree
Showing 32 changed files with 1,185 additions and 809 deletions.
1 change: 1 addition & 0 deletions nuget/helpers/lib/NuGetUpdater/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tab_width = 4

# New line preferences
insert_final_newline = true
#end_of_line = lf

#### .NET Coding Conventions ####
[*.{cs,vb}]
Expand Down
7 changes: 5 additions & 2 deletions nuget/helpers/lib/NuGetUpdater/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@

<PackageVersion Include="GuiLabs.Language.Xml" Version="1.2.60" />

<PackageVersion Include="Microsoft.Build" Version="17.5.0" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.5.0" />
<PackageVersion Include="Microsoft.Build.Locator" Version="1.6.1" />
<PackageVersion Include="Microsoft.Build" Version="17.5.0" ExcludeAssets="Runtime" PrivateAssets="All" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.5.0" ExcludeAssets="Runtime" PrivateAssets="All" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.5.0" />

<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />

<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />

<PackageVersion Include="MSBuild.StructuredLogger" Version="2.2.317" />

<PackageVersion Include="NuGet.Core" Version="2.14.0-rtm-832" Aliases="CoreV2" />

<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ await RunAsync(path =>
"--output",
Path.Combine(path, DiscoveryWorker.DiscoveryResultFileName),
],
packages: [],
packages:
[
MockNuGetPackage.CreateSimplePackage("Some.Package", "1.2.3", "net8.0"),
],
initialFiles:
[
("path/to/some directory with spaces/project.csproj", """
<Project Sdk="Microsoft.NETSdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
Expand All @@ -51,8 +54,6 @@ await RunAsync(path =>
{
FilePath = "project.csproj",
TargetFrameworks = ["net8.0"],
ReferencedProjectPaths = [],
ExpectedDependencyCount = 2,
Dependencies = [
new("Some.Package", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
],
Expand Down Expand Up @@ -140,14 +141,10 @@ await RunAsync(path =>
{
FilePath = "path/to/my.csproj",
TargetFrameworks = ["net45"],
ReferencedProjectPaths = [],
ExpectedDependencyCount = 2,
Dependencies = [
new("Some.Package", "7.0.1", DependencyType.PackagesConfig, TargetFrameworks: ["net45"]),
],
Properties = [
new("TargetFrameworkVersion", "v4.5", "path/to/my.csproj"),
],
Properties = [],
}
]
}
Expand Down Expand Up @@ -206,14 +203,10 @@ await RunAsync(path =>
{
FilePath = "my.csproj",
TargetFrameworks = ["net45"],
ReferencedProjectPaths = [],
ExpectedDependencyCount = 2,
Dependencies = [
new("Some.Package", "7.0.1", DependencyType.PackagesConfig, TargetFrameworks: ["net45"])
],
Properties = [
new("TargetFrameworkVersion", "v4.5", "path/to/my.csproj"),
],
Properties = [],
}
]
}
Expand Down Expand Up @@ -273,14 +266,10 @@ await RunAsync(path =>
{
FilePath = "my.csproj",
TargetFrameworks = ["net45"],
ReferencedProjectPaths = [],
ExpectedDependencyCount = 2,
Dependencies = [
new("Some.Package", "7.0.1", DependencyType.PackagesConfig, TargetFrameworks: ["net45"])
],
Properties = [
new("TargetFrameworkVersion", "v4.5", "path/to/my.csproj"),
],
Properties = [],
}
]
}
Expand All @@ -303,27 +292,32 @@ await RunAsync(path =>
new[]
{
("path/to/my.csproj", """
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="7.0.1" />
<PackageReference Include="Package.A" Version="1.2.3" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
"""),
("path/Directory.Build.props", """
<Project>
<ItemGroup Condition="'$(ManagePackageVersionsCentrally)' == 'true'">
<GlobalPackageReference Include="System.Text.Json" Version="8.0.3" />
</ItemGroup>
<ItemGroup Condition="'$(ManagePackageVersionsCentrally)' != 'true'">
<PackageReference Include="System.Text.Json" Version="8.0.3" />
<PackageReference Include="Package.B" Version="4.5.6" />
</ItemGroup>
<ItemGroup Condition="'$(ManagePackageVersionsCentrally)' == 'true'">
<GlobalPackageReference Include="Package.B" Version="7.8.9" />
</ItemGroup>
</Project>
""")
},
packages:
[
MockNuGetPackage.CreateSimplePackage("Package.A", "1.2.3", "net8.0"),
MockNuGetPackage.CreateSimplePackage("Package.B", "4.5.6", "net8.0"),
],
expectedResult: new()
{
Path = "path/to",
Expand All @@ -332,28 +326,19 @@ await RunAsync(path =>
{
FilePath = "my.csproj",
TargetFrameworks = ["net8.0"],
ReferencedProjectPaths = [],
ExpectedDependencyCount = 2,
Dependencies = [
new("Newtonsoft.Json", "7.0.1", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
// $(ManagePackageVersionsCentrally) evaluates false by default, we only get a PackageReference
new("System.Text.Json", "8.0.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0"])
new("Package.A", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
new("Package.B", "4.5.6", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
],
Properties = [
new("ManagePackageVersionsCentrally", "false", "path/to/my.csproj"),
new("TargetFramework", "net8.0", "path/to/my.csproj"),
],
},
new()
{
FilePath = "../Directory.Build.props",
ReferencedProjectPaths = [],
ExpectedDependencyCount = 2,
Dependencies = [
new("System.Text.Json", "8.0.3", DependencyType.PackageReference, IsDirect: true),
new("System.Text.Json", "8.0.3", DependencyType.GlobalPackageReference, IsDirect: true)
],
Properties = [],
}
],
ImportedFiles = [
"../Directory.Build.props"
]
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
using NuGetUpdater.Core.Discover;
using NuGetUpdater.Core.Test.Update;
using NuGetUpdater.Core.Test.Utilities;
using NuGetUpdater.Core.Utilities;

using Xunit;

namespace NuGetUpdater.Core.Test.Discover;

using TestFile = (string Path, string Content);

public class DiscoveryWorkerTestBase
public class DiscoveryWorkerTestBase : TestBase
{
protected static async Task TestDiscoveryAsync(
string workspacePath,
Expand All @@ -35,17 +36,17 @@ protected static void ValidateWorkspaceResult(ExpectedWorkspaceDiscoveryResult e
{
Assert.NotNull(actualResult);
Assert.Equal(expectedResult.Path.NormalizePathToUnix(), actualResult.Path.NormalizePathToUnix());
ValidateDirectoryPackagesProps(expectedResult.DirectoryPackagesProps, actualResult.DirectoryPackagesProps);
ValidateResultWithDependencies(expectedResult.GlobalJson, actualResult.GlobalJson);
ValidateResultWithDependencies(expectedResult.DotNetToolsJson, actualResult.DotNetToolsJson);
ValidateProjectResults(expectedResult.Projects, actualResult.Projects);
AssertEx.Equal(expectedResult.ImportedFiles, actualResult.ImportedFiles, PathComparer.Instance);
Assert.Equal(expectedResult.ExpectedProjectCount ?? expectedResult.Projects.Length, actualResult.Projects.Length);
Assert.Equal(expectedResult.ErrorType, actualResult.ErrorType);
Assert.Equal(expectedResult.ErrorDetails, actualResult.ErrorDetails);

return;

void ValidateResultWithDependencies(ExpectedDependencyDiscoveryResult? expectedResult, IDiscoveryResultWithDependencies? actualResult)
static void ValidateResultWithDependencies(ExpectedDependencyDiscoveryResult? expectedResult, IDiscoveryResultWithDependencies? actualResult)
{
if (expectedResult is null)
{
Expand All @@ -61,50 +62,61 @@ void ValidateResultWithDependencies(ExpectedDependencyDiscoveryResult? expectedR
ValidateDependencies(expectedResult.Dependencies, actualResult.Dependencies);
Assert.Equal(expectedResult.ExpectedDependencyCount ?? expectedResult.Dependencies.Length, actualResult.Dependencies.Length);
}
}

void ValidateProjectResults(ImmutableArray<ExpectedSdkProjectDiscoveryResult> expectedProjects, ImmutableArray<ProjectDiscoveryResult> actualProjects)
internal static void ValidateProjectResults(ImmutableArray<ExpectedSdkProjectDiscoveryResult> expectedProjects, ImmutableArray<ProjectDiscoveryResult> actualProjects)
{
if (expectedProjects.IsDefaultOrEmpty)
{
if (expectedProjects.IsDefaultOrEmpty)
{
return;
}
return;
}

foreach (var expectedProject in expectedProjects)
foreach (var expectedProject in expectedProjects)
{
var actualProject = actualProjects.SingleOrDefault(p => p.FilePath.NormalizePathToUnix() == expectedProject.FilePath.NormalizePathToUnix());
Assert.True(actualProject is not null, $"Unable to find project with path `{expectedProject.FilePath.NormalizePathToUnix()}` in collection [{string.Join(", ", actualProjects.Select(p => p.FilePath))}]");
Assert.Equal(expectedProject.FilePath.NormalizePathToUnix(), actualProject.FilePath.NormalizePathToUnix());
AssertEx.Equal(expectedProject.Properties, actualProject.Properties, PropertyComparer.Instance);
AssertEx.Equal(expectedProject.TargetFrameworks, actualProject.TargetFrameworks);
AssertEx.Equal(expectedProject.ReferencedProjectPaths, actualProject.ReferencedProjectPaths);
if (expectedProject.ImportedFiles is not null)
{
var actualProject = actualProjects.Single(p => p.FilePath.NormalizePathToUnix() == expectedProject.FilePath.NormalizePathToUnix());

Assert.Equal(expectedProject.FilePath.NormalizePathToUnix(), actualProject.FilePath.NormalizePathToUnix());
AssertEx.Equal(expectedProject.Properties, actualProject.Properties, PropertyComparer.Instance);
AssertEx.Equal(expectedProject.TargetFrameworks, actualProject.TargetFrameworks);
AssertEx.Equal(expectedProject.ReferencedProjectPaths.Select(PathHelper.NormalizePathToUnix), actualProject.ReferencedProjectPaths.Select(PathHelper.NormalizePathToUnix));
ValidateDependencies(expectedProject.Dependencies, actualProject.Dependencies);
Assert.Equal(expectedProject.ExpectedDependencyCount ?? expectedProject.Dependencies.Length, actualProject.Dependencies.Length);
AssertEx.Equal(expectedProject.ImportedFiles.Value.Select(PathHelper.NormalizePathToUnix), actualProject.ImportedFiles.Select(PathHelper.NormalizePathToUnix));
}

ValidateDependencies(expectedProject.Dependencies, actualProject.Dependencies);
Assert.Equal(expectedProject.ExpectedDependencyCount ?? expectedProject.Dependencies.Length, actualProject.Dependencies.Length);
}
}

void ValidateDirectoryPackagesProps(ExpectedDirectoryPackagesPropsDiscovertyResult? expected, DirectoryPackagesPropsDiscoveryResult? actual)
internal static void ValidateDependencies(ImmutableArray<Dependency> expectedDependencies, ImmutableArray<Dependency> actualDependencies)
{
if (expectedDependencies.IsDefault)
{
ValidateResultWithDependencies(expected, actual);
Assert.Equal(expected?.IsTransitivePinningEnabled, actual?.IsTransitivePinningEnabled);
return;
}

void ValidateDependencies(ImmutableArray<Dependency> expectedDependencies, ImmutableArray<Dependency> actualDependencies)
foreach (var expectedDependency in expectedDependencies)
{
if (expectedDependencies.IsDefault)
var matchingDependencies = actualDependencies.Where(d =>
{
return;
}

foreach (var expectedDependency in expectedDependencies)
{
var actualDependency = actualDependencies.Single(d => d.Name == expectedDependency.Name && d.Type == expectedDependency.Type);
Assert.Equal(expectedDependency.Name, actualDependency.Name);
Assert.Equal(expectedDependency.Version, actualDependency.Version);
Assert.Equal(expectedDependency.Type, actualDependency.Type);
AssertEx.Equal(expectedDependency.TargetFrameworks, actualDependency.TargetFrameworks);
Assert.Equal(expectedDependency.IsDirect, actualDependency.IsDirect);
Assert.Equal(expectedDependency.IsTransitive, actualDependency.IsTransitive);
}
return d.Name == expectedDependency.Name
&& d.Type == expectedDependency.Type
&& d.Version == expectedDependency.Version
&& d.IsDirect == expectedDependency.IsDirect
&& d.IsTransitive == expectedDependency.IsTransitive
&& d.TargetFrameworks.SequenceEqual(expectedDependency.TargetFrameworks);
}).ToArray();
Assert.True(matchingDependencies.Length == 1, $"""
Unable to find 1 dependency matching; found {matchingDependencies.Length}:
Name: {expectedDependency.Name}
Type: {expectedDependency.Type}
Version: {expectedDependency.Version}
IsDirect: {expectedDependency.IsDirect}
IsTransitive: {expectedDependency.IsTransitive}
TargetFrameworks: {string.Join(", ", expectedDependency.TargetFrameworks ?? [])}
Found:{"\n\t"}{string.Join("\n\t", actualDependencies)}
""");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ await TestDiscoveryAsync(
</packages>
"""),
("myproj.csproj", """
<Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
Expand All @@ -44,7 +44,6 @@ await TestDiscoveryAsync(
],
TargetFrameworks = ["net46"],
Dependencies = [
new("Microsoft.NETFramework.ReferenceAssemblies", "1.0.3", DependencyType.Unknown, TargetFrameworks: ["net46"], IsTransitive: true),
new("Package.A", "1.0.0", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
new("Package.B", "2.0.0", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ public class Proj : DiscoveryWorkerTestBase
public async Task DirsProjExpansion(string itemType)
{
await TestDiscoveryAsync(
packages: [],
packages:
[
MockNuGetPackage.CreateSimplePackage("Package.A", "1.0.0", "net8.0"),
MockNuGetPackage.CreateSimplePackage("Package.B", "2.0.0", "net8.0"),
],
workspacePath: "dependabot",
files:
[
Expand Down Expand Up @@ -60,7 +64,6 @@ await TestDiscoveryAsync(
new()
{
FilePath = "../src/project1/project1.csproj",
ExpectedDependencyCount = 2,
Dependencies =
[
new("Package.A", "1.0.0", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
Expand All @@ -70,12 +73,10 @@ await TestDiscoveryAsync(
new("TargetFramework", "net8.0", "src/project1/project1.csproj")
],
TargetFrameworks = ["net8.0"],
ReferencedProjectPaths = []
},
new()
{
FilePath = "../src/project2/project2.csproj",
ExpectedDependencyCount = 2,
Dependencies =
[
new("Package.B", "2.0.0", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
Expand All @@ -85,7 +86,6 @@ await TestDiscoveryAsync(
new("TargetFramework", "net8.0", "src/project2/project2.csproj")
],
TargetFrameworks = ["net8.0"],
ReferencedProjectPaths = []
}
]
}
Expand Down
Loading

0 comments on commit 8fda407

Please sign in to comment.