Use DependencyContext.CompileLibraries to determine dependency graph (#7626)
* Use DependencyContext.CompileLibraries to determine dependency graph Fixes https://github.com/aspnet/Mvc/issues/7617
This commit is contained in:
parent
9cea47fa7d
commit
e34b4e8335
|
|
@ -51,8 +51,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
|
||||||
IEnumerable<AssemblyItem> assemblyItems;
|
IEnumerable<AssemblyItem> assemblyItems;
|
||||||
|
|
||||||
if (dependencyContext == null)
|
if (dependencyContext == null || dependencyContext.CompileLibraries.Count == 0)
|
||||||
{
|
{
|
||||||
|
// If an application was built with PreserveCompilationContext = false, CompileLibraries will be empty and we
|
||||||
|
// can no longer reliably infer the dependency closure. In this case, treat it the same as a missing
|
||||||
|
// deps file.
|
||||||
assemblyItems = new[] { GetAssemblyItem(entryAssembly) };
|
assemblyItems = new[] { GetAssemblyItem(entryAssembly) };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -151,30 +154,46 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
// Internal for unit testing
|
// Internal for unit testing
|
||||||
internal static IEnumerable<RuntimeLibrary> GetCandidateLibraries(DependencyContext dependencyContext)
|
internal static IEnumerable<RuntimeLibrary> GetCandidateLibraries(DependencyContext dependencyContext)
|
||||||
{
|
{
|
||||||
var candidatesResolver = new CandidateResolver(dependencyContext.RuntimeLibraries, ReferenceAssemblies);
|
// When using Microsoft.AspNetCore.App \ Microsoft.AspNetCore.All shared runtimes, entries in the RuntimeLibraries
|
||||||
return candidatesResolver.GetCandidates();
|
// get "erased" and it is no longer accurate to query to determine a library's dependency closure.
|
||||||
|
// We'll use CompileLibraries to calculate the dependency graph and runtime library to resolve assemblies to inspect.
|
||||||
|
var candidatesResolver = new CandidateResolver(dependencyContext.CompileLibraries, ReferenceAssemblies);
|
||||||
|
foreach (var library in dependencyContext.RuntimeLibraries)
|
||||||
|
{
|
||||||
|
if (candidatesResolver.IsCandidate(library))
|
||||||
|
{
|
||||||
|
yield return library;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CandidateResolver
|
private class CandidateResolver
|
||||||
{
|
{
|
||||||
private readonly IDictionary<string, Dependency> _runtimeDependencies;
|
private readonly IDictionary<string, Dependency> _compileLibraries;
|
||||||
|
|
||||||
public CandidateResolver(IReadOnlyList<RuntimeLibrary> runtimeDependencies, ISet<string> referenceAssemblies)
|
public CandidateResolver(IReadOnlyList<Library> compileLibraries, ISet<string> referenceAssemblies)
|
||||||
{
|
{
|
||||||
var dependenciesWithNoDuplicates = new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase);
|
var compileLibrariesWithNoDuplicates = new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var dependency in runtimeDependencies)
|
foreach (var library in compileLibraries)
|
||||||
{
|
{
|
||||||
if (dependenciesWithNoDuplicates.ContainsKey(dependency.Name))
|
if (compileLibrariesWithNoDuplicates.ContainsKey(library.Name))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(Resources.FormatCandidateResolver_DifferentCasedReference(dependency.Name));
|
throw new InvalidOperationException(Resources.FormatCandidateResolver_DifferentCasedReference(library.Name));
|
||||||
}
|
}
|
||||||
dependenciesWithNoDuplicates.Add(dependency.Name, CreateDependency(dependency, referenceAssemblies));
|
|
||||||
|
compileLibrariesWithNoDuplicates.Add(library.Name, CreateDependency(library, referenceAssemblies));
|
||||||
}
|
}
|
||||||
|
|
||||||
_runtimeDependencies = dependenciesWithNoDuplicates;
|
_compileLibraries = compileLibrariesWithNoDuplicates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dependency CreateDependency(RuntimeLibrary library, ISet<string> referenceAssemblies)
|
public bool IsCandidate(Library library)
|
||||||
|
{
|
||||||
|
var classification = ComputeClassification(library.Name);
|
||||||
|
return classification == DependencyClassification.ReferencesMvc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dependency CreateDependency(Library library, ISet<string> referenceAssemblies)
|
||||||
{
|
{
|
||||||
var classification = DependencyClassification.Unknown;
|
var classification = DependencyClassification.Unknown;
|
||||||
if (referenceAssemblies.Contains(library.Name))
|
if (referenceAssemblies.Contains(library.Name))
|
||||||
|
|
@ -187,14 +206,14 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
|
||||||
private DependencyClassification ComputeClassification(string dependency)
|
private DependencyClassification ComputeClassification(string dependency)
|
||||||
{
|
{
|
||||||
if (!_runtimeDependencies.ContainsKey(dependency))
|
if (!_compileLibraries.ContainsKey(dependency))
|
||||||
{
|
{
|
||||||
// Library does not have runtime dependency. Since we can't infer
|
// Library does not have runtime dependency. Since we can't infer
|
||||||
// anything about it's references, we'll assume it does not have a reference to Mvc.
|
// anything about it's references, we'll assume it does not have a reference to Mvc.
|
||||||
return DependencyClassification.DoesNotReferenceMvc;
|
return DependencyClassification.DoesNotReferenceMvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
var candidateEntry = _runtimeDependencies[dependency];
|
var candidateEntry = _compileLibraries[dependency];
|
||||||
if (candidateEntry.Classification != DependencyClassification.Unknown)
|
if (candidateEntry.Classification != DependencyClassification.Unknown)
|
||||||
{
|
{
|
||||||
return candidateEntry.Classification;
|
return candidateEntry.Classification;
|
||||||
|
|
@ -219,26 +238,15 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<RuntimeLibrary> GetCandidates()
|
|
||||||
{
|
|
||||||
foreach (var dependency in _runtimeDependencies)
|
|
||||||
{
|
|
||||||
if (ComputeClassification(dependency.Key) == DependencyClassification.ReferencesMvc)
|
|
||||||
{
|
|
||||||
yield return dependency.Value.Library;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Dependency
|
private class Dependency
|
||||||
{
|
{
|
||||||
public Dependency(RuntimeLibrary library, DependencyClassification classification)
|
public Dependency(Library library, DependencyClassification classification)
|
||||||
{
|
{
|
||||||
Library = library;
|
Library = library;
|
||||||
Classification = classification;
|
Classification = classification;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RuntimeLibrary Library { get; }
|
public Library Library { get; }
|
||||||
|
|
||||||
public DependencyClassification Classification { get; set; }
|
public DependencyClassification Classification { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,34 +176,35 @@ namespace Microsoft.AspNetCore.Mvc.Testing
|
||||||
{
|
{
|
||||||
// The default dependency context will be populated in .net core applications.
|
// The default dependency context will be populated in .net core applications.
|
||||||
var context = DependencyContext.Default;
|
var context = DependencyContext.Default;
|
||||||
if (context != null)
|
if (context == null || context.CompileLibraries.Count == 0)
|
||||||
{
|
|
||||||
// Find the list of projects
|
|
||||||
var projects = context.CompileLibraries.Where(l => l.Type == "project");
|
|
||||||
|
|
||||||
// Find the list of projects runtime information and their assembly names.
|
|
||||||
var runtimeProjectLibraries = context.RuntimeLibraries
|
|
||||||
.Where(r => projects.Any(p => p.Name == r.Name))
|
|
||||||
.ToDictionary(r => r, r => r.GetDefaultAssemblyNames(context).ToArray());
|
|
||||||
|
|
||||||
var entryPointAssemblyName = typeof(TEntryPoint).Assembly.GetName().Name;
|
|
||||||
|
|
||||||
// Find the project containing TEntryPoint
|
|
||||||
var entryPointRuntimeLibrary = runtimeProjectLibraries
|
|
||||||
.Single(rpl => rpl.Value.Any(a => string.Equals(a.Name, entryPointAssemblyName, StringComparison.Ordinal)));
|
|
||||||
|
|
||||||
// Find the list of projects referencing TEntryPoint.
|
|
||||||
var candidates = runtimeProjectLibraries
|
|
||||||
.Where(rpl => rpl.Key.Dependencies
|
|
||||||
.Any(d => string.Equals(d.Name, entryPointRuntimeLibrary.Key.Name, StringComparison.Ordinal)));
|
|
||||||
|
|
||||||
return candidates.SelectMany(rl => rl.Value).Select(Assembly.Load);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// The app domain friendly name will be populated in full framework.
|
// The app domain friendly name will be populated in full framework.
|
||||||
return new[] { Assembly.Load(AppDomain.CurrentDomain.FriendlyName) };
|
return new[] { Assembly.Load(AppDomain.CurrentDomain.FriendlyName) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var runtimeProjectLibraries = context.RuntimeLibraries
|
||||||
|
.ToDictionary(r => r.Name, r => r, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
// Find the list of projects
|
||||||
|
var projects = context.CompileLibraries.Where(l => l.Type == "project");
|
||||||
|
|
||||||
|
var entryPointAssemblyName = typeof(TEntryPoint).Assembly.GetName().Name;
|
||||||
|
|
||||||
|
// Find the list of projects referencing TEntryPoint.
|
||||||
|
var candidates = context.CompileLibraries
|
||||||
|
.Where(library => library.Dependencies.Any(d => string.Equals(d.Name, entryPointAssemblyName, StringComparison.Ordinal)));
|
||||||
|
|
||||||
|
var testAssemblies = new List<Assembly>();
|
||||||
|
foreach (var candidate in candidates)
|
||||||
|
{
|
||||||
|
if (runtimeProjectLibraries.TryGetValue(candidate.Name, out var runtimeLibrary))
|
||||||
|
{
|
||||||
|
var runtimeAssemblies = runtimeLibrary.GetDefaultAssemblyNames(context);
|
||||||
|
testAssemblies.AddRange(runtimeAssemblies.Select(Assembly.Load));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return testAssemblies;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyModel;
|
using Microsoft.Extensions.DependencyModel;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
|
@ -28,6 +29,30 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
Assert.Equal(new[] { ThisAssembly }, result);
|
Assert.Equal(new[] { ThisAssembly }, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ResolveAssemblies_ReturnsCurrentAssembly_IfDepsFileDoesNotHaveAnyCompileLibraries()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var runtimeLibraries = new[]
|
||||||
|
{
|
||||||
|
GetRuntimeLibrary("MyApp", "Microsoft.AspNetCore.All"),
|
||||||
|
GetRuntimeLibrary("Microsoft.AspNetCore.All", "Microsoft.NETCore.App"),
|
||||||
|
GetRuntimeLibrary("Microsoft.NETCore.App"),
|
||||||
|
GetRuntimeLibrary("ClassLibrary"),
|
||||||
|
};
|
||||||
|
var dependencyContext = GetDependencyContext(compileLibraries: Array.Empty<CompilationLibrary>(), runtimeLibraries);
|
||||||
|
var provider = new TestApplicationAssembliesProvider
|
||||||
|
{
|
||||||
|
DependencyContext = dependencyContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.ResolveAssemblies(ThisAssembly);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new[] { ThisAssembly }, result);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ResolveAssemblies_ReturnsRelatedAssembliesOrderedByName()
|
public void ResolveAssemblies_ReturnsRelatedAssembliesOrderedByName()
|
||||||
{
|
{
|
||||||
|
|
@ -56,12 +81,14 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
var mvcAssembly = typeof(IActionResult).Assembly;
|
var mvcAssembly = typeof(IActionResult).Assembly;
|
||||||
var classLibrary = typeof(FactAttribute).Assembly;
|
var classLibrary = typeof(FactAttribute).Assembly;
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var libraries = new Dictionary<string, string[]>
|
||||||
{
|
{
|
||||||
GetLibrary(ThisAssembly.GetName().Name, new[] { mvcAssembly.GetName().Name, classLibrary.GetName().Name }),
|
[ThisAssembly.GetName().Name] = new[] { mvcAssembly.GetName().Name, classLibrary.GetName().Name },
|
||||||
GetLibrary(mvcAssembly.GetName().Name),
|
[mvcAssembly.GetName().Name] = Array.Empty<string>(),
|
||||||
GetLibrary(classLibrary.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
[classLibrary.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var dependencyContext = GetDependencyContext(libraries);
|
||||||
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
var provider = new TestApplicationAssembliesProvider
|
||||||
{
|
{
|
||||||
|
|
@ -83,13 +110,15 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
var classLibrary = typeof(object).Assembly;
|
var classLibrary = typeof(object).Assembly;
|
||||||
var relatedPart = typeof(FactAttribute).Assembly;
|
var relatedPart = typeof(FactAttribute).Assembly;
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var libraries = new Dictionary<string, string[]>
|
||||||
{
|
{
|
||||||
GetLibrary(ThisAssembly.GetName().Name, new[] { relatedPart.GetName().Name, classLibrary.GetName().Name }),
|
[ThisAssembly.GetName().Name] = new[] { relatedPart.GetName().Name, classLibrary.GetName().Name },
|
||||||
GetLibrary(classLibrary.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
[classLibrary.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
||||||
GetLibrary(relatedPart.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
[relatedPart.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
||||||
GetLibrary(mvcAssembly.GetName().Name),
|
[mvcAssembly.GetName().Name] = Array.Empty<string>(),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var dependencyContext = GetDependencyContext(libraries);
|
||||||
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
var provider = new TestApplicationAssembliesProvider
|
||||||
{
|
{
|
||||||
|
|
@ -136,8 +165,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mvcAssembly = typeof(IActionResult).Assembly;
|
var mvcAssembly = typeof(IActionResult).Assembly;
|
||||||
var libraryAssembly1 = typeof(object).Assembly;
|
var libraryAssembly1 = typeof(HttpContext).Assembly;
|
||||||
var libraryAssembly2 = typeof(HttpContext).Assembly;
|
var libraryAssembly2 = typeof(JsonConverter).Assembly;
|
||||||
var relatedPart = typeof(FactAttribute).Assembly;
|
var relatedPart = typeof(FactAttribute).Assembly;
|
||||||
var expected = string.Join(
|
var expected = string.Join(
|
||||||
Environment.NewLine,
|
Environment.NewLine,
|
||||||
|
|
@ -145,13 +174,15 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
libraryAssembly1.FullName,
|
libraryAssembly1.FullName,
|
||||||
libraryAssembly2.FullName);
|
libraryAssembly2.FullName);
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var libraries = new Dictionary<string, string[]>
|
||||||
{
|
{
|
||||||
GetLibrary(ThisAssembly.GetName().Name, new[] { relatedPart.GetName().Name, libraryAssembly1.GetName().Name }),
|
[ThisAssembly.GetName().Name] = new[] { relatedPart.GetName().Name, libraryAssembly1.GetName().Name },
|
||||||
GetLibrary(libraryAssembly1.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
[libraryAssembly1.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
||||||
GetLibrary(libraryAssembly2.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
[libraryAssembly2.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
||||||
GetLibrary(mvcAssembly.GetName().Name),
|
[mvcAssembly.GetName().Name] = Array.Empty<string>(),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var dependencyContext = GetDependencyContext(libraries);
|
||||||
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
var provider = new TestApplicationAssembliesProvider
|
||||||
{
|
{
|
||||||
|
|
@ -179,31 +210,41 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
var upperCaseLibrary = "Microsoft.AspNetCore.Mvc";
|
var upperCaseLibrary = "Microsoft.AspNetCore.Mvc";
|
||||||
var mixedCaseLibrary = "microsoft.aspNetCore.mvc";
|
var mixedCaseLibrary = "microsoft.aspNetCore.mvc";
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var libraries = new Dictionary<string, string[]>
|
||||||
{
|
{
|
||||||
GetLibrary(mixedCaseLibrary),
|
[upperCaseLibrary] = Array.Empty<string>(),
|
||||||
GetLibrary(upperCaseLibrary),
|
[mixedCaseLibrary] = Array.Empty<string>(),
|
||||||
});
|
};
|
||||||
|
var dependencyContext = GetDependencyContext(libraries);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exception = Assert.Throws<InvalidOperationException>(() => ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext));
|
var exception = Assert.Throws<InvalidOperationException>(() => ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext).ToArray());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal($"A duplicate entry for library reference {upperCaseLibrary} was found. Please check that all package references in all projects use the same casing for the same package references.", exception.Message);
|
Assert.Equal($"A duplicate entry for library reference {mixedCaseLibrary} was found. Please check that all package references in all projects use the same casing for the same package references.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetCandidateLibraries_IgnoresMvcAssemblies()
|
public void GetCandidateLibraries_IgnoresMvcAssemblies()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expected = GetLibrary("SomeRandomAssembly", "Microsoft.AspNetCore.Mvc.Abstractions");
|
var expected = GetRuntimeLibrary("SomeRandomAssembly", "Microsoft.AspNetCore.Mvc.Abstractions");
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var runtimeLibraries = new[]
|
||||||
{
|
{
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc"),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||||
expected,
|
expected,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var compileLibraries = new[]
|
||||||
|
{
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.Mvc"),
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||||
|
GetCompileLibrary("SomeRandomAssembly", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||||
|
};
|
||||||
|
var dependencyContext = GetDependencyContext(compileLibraries, runtimeLibraries);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||||
|
|
@ -213,16 +254,86 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetCandidateLibraries_DoesNotThrow_IfLibraryDoesNotHaveRuntimeComponent()
|
public void GetCandidateLibraries_ReturnsRuntimeLibraries_IfCompileLibraryDependencyToMvcIsPresent()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expected = GetLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc");
|
// When an app is running against Microsoft.AspNetCore.All shared runtime or if the DependencyContext is queried
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
// from an app that's running on Microsoft.NETCore.App (e.g. in a unit testing scenario), the
|
||||||
|
// runtime library does not state that the app references Mvc whereas the compile library does. This test validates
|
||||||
|
// that we correctly recognize this scenario.
|
||||||
|
var expected = GetRuntimeLibrary("MyApp", "Microsoft.AspNetCore.All");
|
||||||
|
var runtimeLibraries = new[]
|
||||||
{
|
{
|
||||||
expected,
|
expected,
|
||||||
GetLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
GetRuntimeLibrary("Microsoft.AspNetCore.All", "Microsoft.NETCore.App"),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
GetRuntimeLibrary("Microsoft.NETCore.App"),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var compileLibraries = new[]
|
||||||
|
{
|
||||||
|
GetCompileLibrary("MyApp", "Microsoft.AspNetCore.All"),
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.All", "Microsoft.AspNetCore.Mvc", "Microsoft.NETCore.App"),
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.Mvc"),
|
||||||
|
GetCompileLibrary("Microsoft.NETCore.App"),
|
||||||
|
};
|
||||||
|
var dependencyContext = GetDependencyContext(compileLibraries, runtimeLibraries);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new[] { expected }, candidates);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetCandidateLibraries_DoesNotThrow_IfLibraryDoesNotAppearAsCompileOrRuntimeLibrary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = GetRuntimeLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc");
|
||||||
|
var compileLibraries = new[]
|
||||||
|
{
|
||||||
|
GetCompileLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc"),
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.Mvc"),
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var runtimeLibraries = new[]
|
||||||
|
{
|
||||||
|
expected,
|
||||||
|
GetRuntimeLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
||||||
|
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var dependencyContext = GetDependencyContext(compileLibraries, runtimeLibraries);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext).ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new[] { expected }, candidates);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetCandidateLibraries_DoesNotThrow_IfCompileLibraryIsPresentButNotRuntimeLibrary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = GetRuntimeLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc");
|
||||||
|
var compileLibraries = new[]
|
||||||
|
{
|
||||||
|
GetCompileLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc"),
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.Mvc"),
|
||||||
|
GetCompileLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
||||||
|
GetCompileLibrary("Libuv"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var runtimeLibraries = new[]
|
||||||
|
{
|
||||||
|
expected,
|
||||||
|
GetRuntimeLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
||||||
|
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var dependencyContext = GetDependencyContext(compileLibraries, runtimeLibraries);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext).ToList();
|
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext).ToList();
|
||||||
|
|
@ -235,78 +346,81 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
public void GetCandidateLibraries_ReturnsLibrariesReferencingAnyMvcAssembly()
|
public void GetCandidateLibraries_ReturnsLibrariesReferencingAnyMvcAssembly()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var libraries = new Dictionary<string, string[]>
|
||||||
{
|
{
|
||||||
GetLibrary("Foo", "Microsoft.AspNetCore.Mvc.Core"),
|
["Foo"] = new[] { "Microsoft.AspNetCore.Mvc.Core" },
|
||||||
GetLibrary("Bar", "Microsoft.AspNetCore.Mvc"),
|
["Bar"] = new[] { "Microsoft.AspNetCore.Mvc" },
|
||||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
["Qux"] = new[] { "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc" },
|
||||||
GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
["Baz"] = new[] { "Microsoft.AspNetCore.Mvc.Abstractions" },
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
["Microsoft.AspNetCore.Mvc.Core"] = Array.Empty<string>(),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
["Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
||||||
GetLibrary("Not.Mvc.Assembly"),
|
["Not.Mvc.Assembly"] = Array.Empty<string>(),
|
||||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
["Unofficial.Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
["Microsoft.AspNetCore.Mvc.Abstractions"] = Array.Empty<string>(),
|
||||||
});
|
};
|
||||||
|
var dependencyContext = GetDependencyContext(libraries);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(new[] { "Foo", "Bar", "Baz" }, candidates.Select(a => a.Name));
|
Assert.Equal(new[] { "Bar", "Baz", "Foo", }, candidates.Select(a => a.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetCandidateLibraries_LibraryNameComparisonsAreCaseInsensitive()
|
public void GetCandidateLibraries_LibraryNameComparisonsAreCaseInsensitive()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var libraries = new Dictionary<string, string[]>
|
||||||
{
|
{
|
||||||
GetLibrary("Foo", "MICROSOFT.ASPNETCORE.MVC.CORE"),
|
["Foo"] = new[] { "MICROSOFT.ASPNETCORE.MVC.CORE" },
|
||||||
GetLibrary("Bar", "microsoft.aspnetcore.mvc"),
|
["Bar"] = new[] { "microsoft.aspnetcore.mvc" },
|
||||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
["Qux"] = new[] { "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc" },
|
||||||
GetLibrary("Baz", "mIcRoSoFt.AsPnEtCoRe.MvC.aBsTrAcTiOnS"),
|
["Baz"] = new[] { "mIcRoSoFt.AsPnEtCoRe.MvC.aBsTrAcTiOnS" },
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
["Microsoft.AspNetCore.Mvc.Core"] = Array.Empty<string>(),
|
||||||
GetLibrary("LibraryA", "LIBRARYB"),
|
["LibraryA"] = new[] { "LIBRARYB" },
|
||||||
GetLibrary("LibraryB", "microsoft.aspnetcore.mvc"),
|
["LibraryB"] = new[] { "microsoft.aspnetcore.mvc" },
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
["Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
||||||
GetLibrary("Not.Mvc.Assembly"),
|
["Not.Mvc.Assembly"] = Array.Empty<string>(),
|
||||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
["Unofficial.Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
["Microsoft.AspNetCore.Mvc.Abstractions"] = Array.Empty<string>(),
|
||||||
});
|
};
|
||||||
|
var dependencyContext = GetDependencyContext(libraries);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(new[] { "Foo", "Bar", "Baz", "LibraryA", "LibraryB" }, candidates.Select(a => a.Name));
|
Assert.Equal(new[] { "Bar", "Baz", "Foo", "LibraryA", "LibraryB" }, candidates.Select(a => a.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetCandidateLibraries_ReturnsLibrariesWithTransitiveReferencesToAnyMvcAssembly()
|
public void GetCandidateLibraries_ReturnsLibrariesWithTransitiveReferencesToAnyMvcAssembly()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedLibraries = new[] { "Foo", "Bar", "Baz", "LibraryA", "LibraryB", "LibraryC", "LibraryE", "LibraryG", "LibraryH" };
|
var expectedLibraries = new[] { "Bar", "Baz", "Foo", "LibraryA", "LibraryB", "LibraryC", "LibraryE", "LibraryG", "LibraryH" };
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var libraries = new Dictionary<string, string[]>
|
||||||
{
|
{
|
||||||
GetLibrary("Foo", "Bar"),
|
["Foo"] = new[] { "Bar" },
|
||||||
GetLibrary("Bar", "Microsoft.AspNetCore.Mvc"),
|
["Bar"] = new[] { "Microsoft.AspNetCore.Mvc" },
|
||||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
["Qux"] = new[] { "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc" },
|
||||||
GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
["Baz"] = new[] { "Microsoft.AspNetCore.Mvc.Abstractions" },
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
["Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
||||||
GetLibrary("Not.Mvc.Assembly"),
|
["Not.Mvc.Assembly"] = Array.Empty<string>(),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
["Microsoft.AspNetCore.Mvc.Abstractions"] = Array.Empty<string>(),
|
||||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
["Unofficial.Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
||||||
GetLibrary("LibraryA", "LibraryB"),
|
["LibraryA"] = new[] { "LibraryB" },
|
||||||
GetLibrary("LibraryB","LibraryC"),
|
["LibraryB"] = new[] { "LibraryC" },
|
||||||
GetLibrary("LibraryC", "LibraryD", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
["LibraryC"] = new[] { "LibraryD", "Microsoft.AspNetCore.Mvc.Abstractions" },
|
||||||
GetLibrary("LibraryD"),
|
["LibraryD"] = Array.Empty<string>(),
|
||||||
GetLibrary("LibraryE","LibraryF","LibraryG"),
|
["LibraryE"] = new[] { "LibraryF", "LibraryG" },
|
||||||
GetLibrary("LibraryF"),
|
["LibraryF"] = Array.Empty<string>(),
|
||||||
GetLibrary("LibraryG", "LibraryH"),
|
["LibraryG"] = new[] { "LibraryH" },
|
||||||
GetLibrary("LibraryH", "LibraryI", "Microsoft.AspNetCore.Mvc"),
|
["LibraryH"] = new[] { "LibraryI", "Microsoft.AspNetCore.Mvc" },
|
||||||
GetLibrary("LibraryI"),
|
["LibraryI"] = Array.Empty<string>(),
|
||||||
});
|
};
|
||||||
|
var dependencyContext = GetDependencyContext(libraries);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||||
|
|
@ -319,23 +433,25 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
public void GetCandidateLibraries_SkipsMvcAssemblies()
|
public void GetCandidateLibraries_SkipsMvcAssemblies()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var dependencyContext = GetDependencyContext(new[]
|
var libraries = new Dictionary<string, string[]>
|
||||||
{
|
{
|
||||||
GetLibrary("MvcSandbox", "Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.Mvc"),
|
["MvcSandbox"] = new[] { "Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.Mvc" },
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.HttpAbstractions"),
|
["Microsoft.AspNetCore.Mvc.Core"] = new[] { "Microsoft.AspNetCore.HttpAbstractions" },
|
||||||
GetLibrary("Microsoft.AspNetCore.HttpAbstractions"),
|
["Microsoft.AspNetCore.HttpAbstractions"] = Array.Empty<string>(),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc", "Microsoft.AspNetCore.Mvc.Abstractions", "Microsoft.AspNetCore.Mvc.Core"),
|
["Microsoft.AspNetCore.Mvc"] = new[] { "Microsoft.AspNetCore.Mvc.Abstractions", "Microsoft.AspNetCore.Mvc.Core" },
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
["Microsoft.AspNetCore.Mvc.Abstractions"] = Array.Empty<string>(),
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.TagHelpers", "Microsoft.AspNetCore.Mvc.Razor"),
|
["Microsoft.AspNetCore.Mvc.TagHelpers"] = new[] { "Microsoft.AspNetCore.Mvc.Razor" },
|
||||||
GetLibrary("Microsoft.AspNetCore.Mvc.Razor"),
|
["Microsoft.AspNetCore.Mvc.Razor"] = Array.Empty<string>(),
|
||||||
GetLibrary("ControllersAssembly", "Microsoft.AspNetCore.Mvc"),
|
["ControllersAssembly"] = new[] { "Microsoft.AspNetCore.Mvc" },
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var dependencyContext = GetDependencyContext(libraries);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(new[] { "MvcSandbox", "ControllersAssembly" }, candidates.Select(a => a.Name));
|
Assert.Equal(new[] { "ControllersAssembly", "MvcSandbox" }, candidates.Select(a => a.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test verifies DefaultAssemblyPartDiscoveryProvider.ReferenceAssemblies reflects the actual loadable assemblies
|
// This test verifies DefaultAssemblyPartDiscoveryProvider.ReferenceAssemblies reflects the actual loadable assemblies
|
||||||
|
|
@ -397,18 +513,37 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DependencyContext GetDependencyContext(RuntimeLibrary[] libraries)
|
private static DependencyContext GetDependencyContext(IDictionary<string, string[]> libraries)
|
||||||
|
{
|
||||||
|
var compileLibraries = new List<CompilationLibrary>();
|
||||||
|
var runtimeLibraries = new List<RuntimeLibrary>();
|
||||||
|
|
||||||
|
foreach (var kvp in libraries.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
var compileLibrary = GetCompileLibrary(kvp.Key, kvp.Value);
|
||||||
|
compileLibraries.Add(compileLibrary);
|
||||||
|
|
||||||
|
var runtimeLibrary = GetRuntimeLibrary(kvp.Key, kvp.Value);
|
||||||
|
runtimeLibraries.Add(runtimeLibrary);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetDependencyContext(compileLibraries, runtimeLibraries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DependencyContext GetDependencyContext(
|
||||||
|
IReadOnlyList<CompilationLibrary> compileLibraries,
|
||||||
|
IReadOnlyList<RuntimeLibrary> runtimeLibraries)
|
||||||
{
|
{
|
||||||
var dependencyContext = new DependencyContext(
|
var dependencyContext = new DependencyContext(
|
||||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||||
CompilationOptions.Default,
|
CompilationOptions.Default,
|
||||||
new CompilationLibrary[0],
|
compileLibraries,
|
||||||
libraries,
|
runtimeLibraries,
|
||||||
Enumerable.Empty<RuntimeFallbacks>());
|
Enumerable.Empty<RuntimeFallbacks>());
|
||||||
return dependencyContext;
|
return dependencyContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RuntimeLibrary GetLibrary(string name, params string[] dependencyNames)
|
private static RuntimeLibrary GetRuntimeLibrary(string name, params string[] dependencyNames)
|
||||||
{
|
{
|
||||||
var dependencies = dependencyNames?.Select(d => new Dependency(d, "42.0.0")) ?? new Dependency[0];
|
var dependencies = dependencyNames?.Select(d => new Dependency(d, "42.0.0")) ?? new Dependency[0];
|
||||||
|
|
||||||
|
|
@ -424,6 +559,20 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
serviceable: true);
|
serviceable: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static CompilationLibrary GetCompileLibrary(string name, params string[] dependencyNames)
|
||||||
|
{
|
||||||
|
var dependencies = dependencyNames?.Select(d => new Dependency(d, "42.0.0")) ?? new Dependency[0];
|
||||||
|
|
||||||
|
return new CompilationLibrary(
|
||||||
|
"package",
|
||||||
|
name,
|
||||||
|
"23.0.0",
|
||||||
|
"hash",
|
||||||
|
Enumerable.Empty<string>(),
|
||||||
|
dependencies: dependencies.ToArray(),
|
||||||
|
serviceable: true);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestAssembly : Assembly
|
private class TestAssembly : Assembly
|
||||||
{
|
{
|
||||||
public override string FullName => "TestRelatedAssembly";
|
public override string FullName => "TestRelatedAssembly";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue