Skip to content
Draft
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
105 changes: 105 additions & 0 deletions Mono.ApiTools.NuGetDiff.Tests/NuGetManagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using NuGet.Versioning;
using System;
using System.IO;
using System.Threading.Tasks;
using Xunit;

namespace Mono.ApiTools.Tests
{
public class NuGetManagerTests
{
[Fact]
public async Task TestNuGetManagerCanOpenPackage()
{
var manager = new NuGetManager();

using (var reader = await manager.OpenPackageAsync("Newtonsoft.Json", "13.0.1"))
{
Assert.NotNull(reader);
var identity = await reader.GetIdentityAsync(default);
Assert.Equal("Newtonsoft.Json", identity.Id);
Assert.Equal(NuGetVersion.Parse("13.0.1"), identity.Version);
}
}

[Fact]
public async Task TestNuGetManagerCanExtractPackage()
{
var manager = new NuGetManager();
var outputDir = Path.Combine(Path.GetTempPath(), "Mono.ApiTools.NuGetDiff", Path.GetRandomFileName());

try
{
await manager.ExtractPackageToDirectoryAsync("Newtonsoft.Json", "13.0.1", outputDir);

Assert.True(Directory.Exists(outputDir));
Assert.NotEmpty(Directory.GetFiles(outputDir, "*.nuspec", SearchOption.AllDirectories));
}
finally
{
if (Directory.Exists(outputDir))
{
try
{
Directory.Delete(outputDir, true);
}
catch
{
// Ignore cleanup errors
}
}
}
}

[Fact]
public async Task TestNuGetManagerCanExtractCachedPackage()
{
var manager = new NuGetManager();
manager.PackageCache = Path.Combine(Path.GetTempPath(), "Mono.ApiTools.NuGetDiff", "cache", Path.GetRandomFileName());

try
{
var dir = await manager.ExtractCachedPackageAsync("Newtonsoft.Json", "13.0.1");

Assert.True(Directory.Exists(dir));
Assert.NotEmpty(Directory.GetFiles(dir, "*.nuspec", SearchOption.AllDirectories));
}
finally
{
if (Directory.Exists(manager.PackageCache))
{
try
{
Directory.Delete(manager.PackageCache, true);
}
catch
{
// Ignore cleanup errors
}
}
}
}

[Fact]
public void TestNuGetManagerGetCachedPackagePath()
{
var manager = new NuGetManager();
manager.PackageCache = "packages";

var path = manager.GetCachedPackagePath("Newtonsoft.Json", "13.0.1");

Assert.Equal("packages/newtonsoft.json/13.0.1/newtonsoft.json.13.0.1.nupkg", path.Replace('\\', '/'));
}

[Fact]
public void TestNuGetManagerGetCachedPackageDirectory()
{
var manager = new NuGetManager();
manager.PackageCache = "packages";

var dir = manager.GetCachedPackageDirectory("Newtonsoft.Json", "13.0.1");

Assert.Equal("packages/newtonsoft.json/13.0.1", dir.Replace('\\', '/'));
}
}
}
197 changes: 3 additions & 194 deletions Mono.ApiTools.NuGetDiff/NuGetDiff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@

namespace Mono.ApiTools
{
public class NuGetDiff
public class NuGetDiff : NuGetManager
{
internal const string NuGetSourceUrl = "https://api.nuget.org/v3/index.json";

private const int DefaultSaveBufferSize = 1024;
private const int DefaultCopyBufferSize = 81920;
private static readonly Encoding UTF8NoBOM = new UTF8Encoding(false, true);
Expand All @@ -29,20 +27,15 @@ public class NuGetDiff
private const string DefaultHtmlDiffFileExtension = ".diff.html";
private const string DefaultMarkdownDiffFileExtension = ".diff.md";
private const string DefaultApiInfoFileExtension = ".info.xml";
private readonly SourceRepository source;
private readonly SourceCacheContext cache;
private readonly ILogger logger;

public NuGetDiff()
: this(NuGetSourceUrl)
: base()
{
}

public NuGetDiff(string sourceUrl)
: base(sourceUrl)
{
source = Repository.Factory.GetCoreV3(sourceUrl);
cache = new SourceCacheContext();
logger = NullLogger.Instance;
}


Expand All @@ -52,8 +45,6 @@ public NuGetDiff(string sourceUrl)

public List<string> IgnoreMemberRegex { get; set; } = new List<string>();

public string PackageCache { get; set; } = "packages";

public bool IgnoreResolutionErrors { get; set; } = false;

public bool IgnoreInheritedInterfaces { get; set; } = false;
Expand Down Expand Up @@ -584,109 +575,6 @@ public async Task SaveCompleteDiffToDirectoryAsync(PackageArchiveReader oldReade
}


// OpenPackageAsync

public Task<PackageArchiveReader> OpenPackageAsync(string id, string version, CancellationToken cancellationToken = default)
{
var identity = new PackageIdentity(id, NuGetVersion.Parse(version));
return OpenPackageAsync(identity, cancellationToken);
}

public Task<PackageArchiveReader> OpenPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default)
{
var identity = new PackageIdentity(id, version);
return OpenPackageAsync(identity, cancellationToken);
}

public async Task<PackageArchiveReader> OpenPackageAsync(PackageIdentity identity, CancellationToken cancellationToken = default)
{
var nupkgPath = await GetPackagePathAsync(identity, cancellationToken).ConfigureAwait(false);
return new PackageArchiveReader(nupkgPath);
}


// ExtractPackageToDirectoryAsync

public Task ExtractPackageToDirectoryAsync(string id, string version, string outputDirectory, CancellationToken cancellationToken = default)
{
var identity = new PackageIdentity(id, NuGetVersion.Parse(version));
return ExtractPackageToDirectoryAsync(identity, outputDirectory, cancellationToken);
}

public Task ExtractPackageToDirectoryAsync(string id, NuGetVersion version, string outputDirectory, CancellationToken cancellationToken = default)
{
var identity = new PackageIdentity(id, version);
return ExtractPackageToDirectoryAsync(identity, outputDirectory, cancellationToken);
}

public async Task ExtractPackageToDirectoryAsync(PackageIdentity identity, string outputDirectory, CancellationToken cancellationToken = default)
{
var nupkgPath = await GetPackagePathAsync(identity, cancellationToken).ConfigureAwait(false);

using (var reader = await OpenPackageAsync(identity, cancellationToken).ConfigureAwait(false))
{
var files = await reader.GetFilesAsync(cancellationToken);
foreach (var file in files.ToArray())
{
var dest = Path.Combine(outputDirectory, file);
await reader.CopyFilesAsync(outputDirectory, files, ExtractFile, logger, cancellationToken);
}
}

string ExtractFile(string source, string target, Stream stream)
{
var extractDirectory = Path.GetDirectoryName(target);
if (!Directory.Exists(extractDirectory))
Directory.CreateDirectory(extractDirectory);

// the main .nuspec should be all lowercase to make things easy to find and match the clientz
if (Path.GetFileName(source) == source && Path.GetExtension(source).ToLowerInvariant() == ".nuspec")
target = Path.Combine(Path.GetDirectoryName(target), Path.GetFileName(target).ToLower());

// copying files stream-to-stream is less efficient
// attempt to copy using File.Copy if we the source is a file on disk
if (Path.IsPathRooted(source))
File.Copy(source, target, true);
else
stream.CopyToFile(target);

return target;
}
}


// ExtractCachedPackageAsync

public Task<string> ExtractCachedPackageAsync(string id, string version, CancellationToken cancellationToken = default)
{
var identity = new PackageIdentity(id, NuGetVersion.Parse(version));
return ExtractCachedPackageAsync(identity, cancellationToken);
}

public Task<string> ExtractCachedPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default)
{
var identity = new PackageIdentity(id, version);
return ExtractCachedPackageAsync(identity, cancellationToken);
}

public async Task<string> ExtractCachedPackageAsync(PackageIdentity identity, CancellationToken cancellationToken = default)
{
var dir = GetCachedPackageDirectory(identity);

// a quick check to make sure the package has not already beed extracted
var nupkg = GetCachedPackagePath(identity);
var extractedFlag = $"{nupkg}.extracted";
if (File.Exists(nupkg) && File.Exists(extractedFlag))
return dir;

await ExtractPackageToDirectoryAsync(identity, dir, cancellationToken);

File.WriteAllText(extractedFlag, "");

return dir;
}


// OpenAssemblyAsync

public async Task<Stream> OpenAssemblyAsync(PackageArchiveReader reader, string assemblyPath, CancellationToken cancellationToken = default)
Expand All @@ -703,47 +591,6 @@ public async Task<Stream> OpenAssemblyAsync(PackageArchiveReader reader, string
}


// GetPackagePath

public string GetCachedPackagePath(string id, string version)
{
var identity = new PackageIdentity(id, NuGetVersion.Parse(version));
return GetCachedPackagePath(identity);
}

public string GetCachedPackagePath(string id, NuGetVersion version)
{
var identity = new PackageIdentity(id, version);
return GetCachedPackagePath(identity);
}

public string GetCachedPackagePath(PackageIdentity ident)
{
var nupkgDir = GetCachedPackageDirectory(ident);
return Path.Combine(nupkgDir, $"{ident.Id.ToLowerInvariant()}.{ident.Version.ToNormalizedString()}.nupkg");
}


// GetPackageRootDirectory

public string GetCachedPackageDirectory(string id, string version)
{
var identity = new PackageIdentity(id, NuGetVersion.Parse(version));
return GetCachedPackageDirectory(identity);
}

public string GetCachedPackageDirectory(string id, NuGetVersion version)
{
var identity = new PackageIdentity(id, version);
return GetCachedPackageDirectory(identity);
}

public string GetCachedPackageDirectory(PackageIdentity ident)
{
return Path.Combine(PackageCache, GetPackageDirectoryBase(ident));
}


// Private members

internal static NuGetFramework TryMatchFramework(NuGetFramework added, NuGetFramework[] choices)
Expand Down Expand Up @@ -976,39 +823,6 @@ XAttribute CreateAssemblyPresence(string assembly)
}
}

private async Task<string> GetPackagePathAsync(PackageIdentity identity, CancellationToken cancellationToken)
{
var metadataResource = await source.GetResourceAsync<PackageMetadataResource>();

var metadata = await metadataResource.GetMetadataAsync(identity, cache, logger, cancellationToken).ConfigureAwait(false);

if (metadata == null)
throw new ArgumentException($"Package identity is not valid: {identity}", nameof(identity));

var ident = metadata.Identity;

var nupkgDir = GetCachedPackageDirectory(ident);
if (!Directory.Exists(nupkgDir))
Directory.CreateDirectory(nupkgDir);

var byId = await source.GetResourceAsync<FindPackageByIdResource>(cancellationToken).ConfigureAwait(false);

var nupkgPath = GetCachedPackagePath(ident);
var nupkgHashPath = $"{nupkgPath}.sha512";
if (!File.Exists(nupkgPath) || !File.Exists(nupkgHashPath))
{
using (var downloader = await byId.GetPackageDownloaderAsync(ident, cache, logger, cancellationToken).ConfigureAwait(false))
{
await downloader.CopyNupkgFileToAsync(nupkgPath, cancellationToken).ConfigureAwait(false);

var sha512 = await downloader.GetPackageHashAsync("SHA512", cancellationToken).ConfigureAwait(false);
File.WriteAllText(nupkgHashPath, sha512);
}
}

return nupkgPath;
}

private Task<Stream> GenerateAssemblyXmlDiffAsync(Stream oldInfo, Stream newInfo, CancellationToken cancellationToken)
{
return Task.Run(() =>
Expand Down Expand Up @@ -1077,11 +891,6 @@ private string GetOutputFilenameBase(string assembly, bool includePlatform)
return string.Join(Path.DirectorySeparatorChar.ToString(), parts.Skip(skip));
}

private string GetPackageDirectoryBase(PackageIdentity ident)
{
return Path.Combine(ident.Id.ToLowerInvariant(), ident.Version.ToNormalizedString());
}

private async Task SaveToFileAsync(Stream stream, string path, CancellationToken cancellationToken)
{
var directory = Path.GetDirectoryName(path);
Expand Down
Loading
Loading