Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
![Nuget](https://img.shields.io/nuget/v/Verify.Nupkg)
[![Downloads](https://img.shields.io/nuget/dt/Verify.Nupkg)](https://nuget.org/packages/Verify.Nupkg)

Extends [Verify](https://github.com/VerifyTests/Verify) to allow verification of [NuGet .nupkg](https://learn.microsoft.com/en-us/nuget/what-is-nuget) files.
Extends [Verify](https://github.com/VerifyTests/Verify) to allow verification of [NuGet .nupkg](https://learn.microsoft.com/en-us/nuget/what-is-nuget) and [.snupkg (symbol package)](https://learn.microsoft.com/en-us/nuget/create-packages/symbol-packages-snupkg) files.

The plugin does not do a naive binary comparison, as that would cause a large amount of verification churn. Instead,
the contents of the .nuspec file are verified, along with a tree view of the package files.
Expand Down Expand Up @@ -50,8 +50,7 @@ If you want to follow packing best practices (validating a README, reproducible

```csharp
[ModuleInitializer]
public static void Initialize() =>
VerifyNupkg.Initialize();
public static void Initialize() => VerifyNupkg.Initialize();
```

### File path
Expand All @@ -60,7 +59,7 @@ public static void Initialize() =>
[Fact]
public Task VerifyNupkgFile()
{
string packagePath = "path/to/package.nupkg";
string packagePath = "path/to/package.nupkg"; // or .snupkg

VerifySettings settings = new();
settings.UseUniqueDirectory(); // Optional; group files into a directory
Expand Down Expand Up @@ -96,8 +95,15 @@ VerifySettings settings = new();
settings.ScrubNuspec();
```

which itself is a convenience method for `ScrubNuspecVersion()` and `ScrubNuspecCommit()`. Feel free to use them
separately if you'd like to verify either of these values.
which itself is a convenience method for these scrubbers:

- `ScrubNuspecVersion()`
- `ScrubNuspecCommit()`
- `ScrubNuspecSchema()`
- `ScrubNuspecRepositoryUrl()`
- `ScrubNuspecBranch()`

Feel free to use them separately if you'd like to verify any of these values.

### Referencing / locating a package built in the same solution

Expand Down
5 changes: 3 additions & 2 deletions Tests/Verify.Nupkg.Tests/ModuleInitializer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.Build.Utilities.ProjectCreation;
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;

using Microsoft.Build.Utilities.ProjectCreation;

namespace Verify.Nupkg.Tests;

Expand Down
40 changes: 40 additions & 0 deletions Tests/Verify.Nupkg.Tests/SamplePackages/PackageCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,44 @@ public IFileInfo Create(IDirectoryInfo destinationDirectory)

return destinationPackage;
}

/// <summary>
/// Creates the package with symbols in a temp directory and copies both .nupkg and .snupkg to the specified destination directory.
/// </summary>
/// <param name="destinationDirectory">The directory to copy the packages to.</param>
/// <returns>A tuple containing <see cref="IFileInfo"/> for the .nupkg and .snupkg files.</returns>
/// <exception cref="Exception">If the build fails.</exception>
public (IFileInfo Nupkg, IFileInfo Snupkg) CreateWithSymbols(IDirectoryInfo destinationDirectory)
{
IFileInfo destinationPackage = _fs.FileInfo.New(_fs.Path.Combine(destinationDirectory.FullName, $"{Name}.nupkg"));
IFileInfo destinationSymbolPackage = _fs.FileInfo.New(_fs.Path.Combine(destinationDirectory.FullName, $"{Name}.snupkg"));

using (_fs.CreateDisposableDirectory(RetryableTempDirectory.GetRandomTempPath(), dirInfo => new RetryableTempDirectory(dirInfo), out IDirectoryInfo temp))
{
using (PackageRepository.Create(temp.FullName, feeds: new Uri("https://api.nuget.org/v3/index.json")))
{
CreateCore(temp)
.Target(name: "CopyPackageForTests", afterTargets: "Pack")
.Task(name: "Copy", parameters: new Dictionary<string, string?>
{
{ "SourceFiles", @"$(OutputPath)..\$(PackageId).$(PackageVersion).nupkg" },
{ "DestinationFiles", destinationPackage.FullName },
})
.Task(name: "Copy", parameters: new Dictionary<string, string?>
{
{ "SourceFiles", @"$(OutputPath)..\$(PackageId).$(PackageVersion).snupkg" },
{ "DestinationFiles", destinationSymbolPackage.FullName },
})
.Save(_fs.Path.Combine(temp.FullName, $"{Name}.csproj"))
.TryBuild(restore: true, target: "Pack", out bool result, out BuildOutput buildOutput, out IDictionary<string, TargetResult>? outputs);

if (!result)
{
throw new Exception($"Failed to build in path '{temp.FullName}'. Errors: {string.Join(Environment.NewLine, buildOutput.Errors)}");
}
}
}

return (destinationPackage, destinationSymbolPackage);
}
}
2 changes: 2 additions & 0 deletions Tests/Verify.Nupkg.Tests/SamplePackages/SamplePackages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal class SamplePackages
public Lazy<IFileInfo> PackageWithoutRepoUrl { get; private set; }
public Lazy<IFileInfo> PackageWithoutRepoCommit { get; private set; }
public Lazy<IFileInfo> PackageWithoutRepoBranch { get; private set; }
public Lazy<(IFileInfo Nupkg, IFileInfo Snupkg)> SimplePackageWithSymbols { get; private set; }

private SamplePackages()
{
Expand All @@ -32,6 +33,7 @@ private SamplePackages()
PackageWithoutRepoUrl = new Lazy<IFileInfo>(() => new PackageWithoutRepoUrlOrCommitOrBranch().Create(workingDirectory));
PackageWithoutRepoCommit = new Lazy<IFileInfo>(() => new PackageWithoutRepoUrlOrCommitOrBranch().Create(workingDirectory));
PackageWithoutRepoBranch = new Lazy<IFileInfo>(() => new PackageWithoutRepoUrlOrCommitOrBranch().Create(workingDirectory));
SimplePackageWithSymbols = new Lazy<(IFileInfo, IFileInfo)>(() => new SimplePackageWithSymbols().CreateWithSymbols(workingDirectory));
}

private IDirectoryInfo GetWorkingDirectory()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.Build.Utilities.ProjectCreation;
using System.IO.Abstractions;

namespace Verify.Nupkg.Tests;

internal class SimplePackageWithSymbols : PackageCreator
{
public override string Name => GetType().Name;

protected override ProjectCreator CreateCore(IDirectoryInfo workingDirectory)
{
File.WriteAllText(Path.Combine(workingDirectory.FullName, "README.md"), "Hello, World!");

return ProjectCreator.Templates.SdkCsproj()
.Property("TargetFramework", "net8.0")
.Property("PackageReadmeFile", "README.md")
.Property("RepositoryType", "git")
.Property("RepositoryUrl", "https://github.com/MattKotsenas/Verify.Nupkg")
.Property("RepositoryCommit", "0e4d1b598f350b3dc675018d539114d1328189ef")
.Property("RepositoryBranch", "dev")
.Property("IncludeSymbols", "true")
.Property("SymbolPackageFormat", "snupkg")
.ItemNone(include: "README.md", metadata: new Dictionary<string, string?>
{
{ "Pack", "true" },
{ "PackagePath", "\\" },
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/
|-- SimplePackageWithSymbols.nuspec
|-- lib
| |-- net8.0
| | |-- SimplePackageWithSymbols.pdb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<package>
<metadata>
<id>SimplePackageWithSymbols</id>
<version>********</version>
<description>Package Description</description>
<packageTypes>
<packageType name="SymbolsPackage" />
</packageTypes>
<repository type="git" url="https://github.com/********/Verify.Nupkg.git" branch="********" commit="****************************************" />
<dependencies>
<group targetFramework="net8.0" />
</dependencies>
</metadata>
</package>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/
|-- [Content_Types].xml
|-- _rels
| |-- .rels
|-- lib
| |-- net8.0
| | |-- SimplePackageWithSymbols.pdb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<package>
<metadata>
<id>SimplePackageWithSymbols</id>
<version>********</version>
<description>Package Description</description>
<packageTypes>
<packageType name="SymbolsPackage" />
</packageTypes>
<repository type="git" url="https://github.com/********/Verify.Nupkg.git" branch="********" commit="****************************************" />
<dependencies>
<group targetFramework="net8.0" />
</dependencies>
</metadata>
</package>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/
|-- SimplePackageWithSymbols.nuspec
|-- lib
| |-- net8.0
| | |-- SimplePackageWithSymbols.pdb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>SimplePackageWithSymbols</id>
<version>1.0.0</version>
<description>Package Description</description>
<packageTypes>
<packageType name="SymbolsPackage" />
</packageTypes>
<repository type="git" url="https://github.com/********/Verify.Nupkg.git" branch="dev" commit="****************************************" />
<dependencies>
<group targetFramework="net8.0" />
</dependencies>
</metadata>
</package>
34 changes: 34 additions & 0 deletions Tests/Verify.Nupkg.Tests/SnupkgTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace Verify.Nupkg.Tests;

[TestClass]
public partial class SnupkgTests
{
private readonly string _snupkg = SamplePackages.Instance.SimplePackageWithSymbols.Value.Snupkg.FullName;

[TestMethod]
public Task BasicSnupkgTest()
{
return VerifyFile(_snupkg).ScrubNuspec();
}

[TestMethod]
public Task SnupkgWithCustomFileExclusionTest()
{
return VerifyFile(_snupkg)
.ScrubNuspec()
.AddNupkgDiffSettings(settings => settings.ExcludedFiles = [new(@"\.psmdcp$"), new(@"\.nuspec$")]);
}

[TestMethod]
public Task SnupkgWithOnlyOptInScrubbersRun()
{
// In this test we intentionally _do not_ include these scrubbers:
// - Version
// - Schema
// to validate that only scrubbers we opt-in to are applied.
return VerifyFile(_snupkg)
.ScrubNuspecCommit()
.ScrubNuspecRepositoryUrl()
.ScrubNuspecBranch();
}
}
10 changes: 10 additions & 0 deletions Tests/Verify.Nupkg.Tests/Verify.Nupkg.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<TargetFrameworks>$(TargetFrameworks);net8.0;net9.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<!-- Allow downgrade of System.Text.Json to 8.0.x for net472 compatibility. Remove this once STJ is resolved. -->
<NoWarn>$(NoWarn);NU1605;NU1109</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand All @@ -18,6 +20,14 @@
<ProjectReference Include="..\..\Verify.Nupkg\Verify.Nupkg.csproj" AddPackageAsOutput="true" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<!--
Pin System.Text.Json to 8.0.x on net472, as NuGet.Packaging requires 8.x and Microsoft.Build pulls in 9.x
-->
<PackageReference Include="System.Text.Encodings.Web" VersionOverride="8.0.0" />
<PackageReference Include="System.Text.Json" VersionOverride="8.0.5" />
</ItemGroup>

<!--
TODO: This should happen automatically when including Verify.
Unclear why this needs to be added manually in this case.
Expand Down
38 changes: 22 additions & 16 deletions Verify.Nupkg/VerifyNupkg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace VerifyTests;

/// <summary>
/// Plugin for Verify to handle .nupkg files.
/// Plugin for Verify to handle .nupkg and .snupkg files.
/// </summary>
public static class VerifyNupkg
{
Expand All @@ -16,7 +16,7 @@ public static class VerifyNupkg
public static bool Initialized { get; private set; }

/// <summary>
/// Register the .nupkg file converter for Verify.
/// Register the .nupkg and .snupkg file converters for Verify.
/// </summary>
public static void Initialize()
{
Expand All @@ -31,24 +31,30 @@ public static void Initialize()

VerifierSettings.RegisterFileConverter(
fromExtension: "nupkg",
conversion: async (stream, settings) =>
{
NupkgDiffSettings diffSettings = settings.GetNupkgDiffSettingsOrDefault();
conversion: ConvertNuGetPackage);

using var zip = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: true);
VerifierSettings.RegisterFileConverter(
fromExtension: "snupkg",
conversion: ConvertNuGetPackage);
}

private static async Task<ConversionResult> ConvertNuGetPackage(Stream stream, IReadOnlyDictionary<string, object> settings)
{
NupkgDiffSettings diffSettings = settings.GetNupkgDiffSettingsOrDefault();

using var zip = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: true);

using Stream nuspecStream = zip.Entries.Single(e => e.Name.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)).Open();
using StreamReader reader = new(nuspecStream);
using Stream nuspecStream = zip.Entries.Single(e => e.Name.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)).Open();
using StreamReader reader = new(nuspecStream);

string manifest = await reader.ReadToEndAsync();
string contents = zip.ListPackageContents(diffSettings.ExcludedFiles);
string manifest = await reader.ReadToEndAsync();
string contents = zip.ListPackageContents(diffSettings.ExcludedFiles);

Target[] targets = [
new Target(extension: "nuspec", data: manifest, name: "manifest"),
new Target(extension: "txt", data: contents, name: "contents"),
];
Target[] targets = [
new Target(extension: "nuspec", data: manifest, name: "manifest"),
new Target(extension: "txt", data: contents, name: "contents"),
];

return new ConversionResult(info: null, targets: targets);
});
return new ConversionResult(info: null, targets: targets);
}
}