using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using FluentAssertions; using Microsoft.DotNet.ProjectModel; using Microsoft.DotNet.ProjectModel.Compilation; using Microsoft.DotNet.ProjectModel.Graph; using NuGet.Frameworks; using NuGet.Versioning; using Xunit; namespace Microsoft.Extensions.DependencyModel.Tests { public class DependencyContextBuilderTests { private string _referenceAssembliesPath = Path.Combine("reference", "assemblies"); private NuGetFramework _defaultFramework; private string _defaultName = "Library.Name"; private string _defaultHash = "Hash"; private NuGetVersion _defaultVersion = new NuGetVersion(1, 2, 3, new []{"dev"}, string.Empty); public DependencyContext Build(CommonCompilerOptions compilerOptions = null, IEnumerable compilationExports = null, IEnumerable runtimeExports = null, bool portable = false, NuGetFramework target = null, string runtime = null) { _defaultFramework = NuGetFramework.Parse("net451"); return new DependencyContextBuilder(_referenceAssembliesPath).Build( compilerOptions, compilationExports ?? new LibraryExport[] { }, runtimeExports ?? new LibraryExport[] {}, portable, target ?? _defaultFramework, runtime ?? string.Empty); } [Fact] public void PreservesCompilationOptions() { var context = Build(new CommonCompilerOptions() { AllowUnsafe = true, Defines = new[] { "Define", "D" }, DelaySign = true, EmitEntryPoint = true, GenerateXmlDocumentation = true, KeyFile = "Key.snk", LanguageVersion = "C#8", Optimize = true, Platform = "Platform", PublicSign = true, WarningsAsErrors = true }); context.CompilationOptions.AllowUnsafe.Should().Be(true); context.CompilationOptions.DelaySign.Should().Be(true); context.CompilationOptions.EmitEntryPoint.Should().Be(true); context.CompilationOptions.GenerateXmlDocumentation.Should().Be(true); context.CompilationOptions.Optimize.Should().Be(true); context.CompilationOptions.PublicSign.Should().Be(true); context.CompilationOptions.WarningsAsErrors.Should().Be(true); context.CompilationOptions.Defines.Should().BeEquivalentTo(new[] { "Define", "D" }); context.CompilationOptions.KeyFile.Should().Be("Key.snk"); context.CompilationOptions.LanguageVersion.Should().Be("C#8"); context.CompilationOptions.Platform.Should().Be("Platform"); } [Fact] public void AlowsNullCompilationOptions() { var context = Build(compilerOptions: null); context.CompilationOptions.Should().Be(CompilationOptions.Default); } [Fact] public void SetsPortableFlag() { var context = Build(portable: true); context.Target.IsPortable.Should().BeTrue(); } [Fact] public void FillsRuntimeAndTarget() { var context = Build(target: new NuGetFramework("SomeFramework",new Version(1,2)), runtime: "win8-x86"); context.Target.Runtime.Should().Be("win8-x86"); context.Target.Framework.Should().Be("SomeFramework,Version=v1.2"); } [Fact] public void SetsServiceableToTrueForPackageDescriptions() { var context = Build(runtimeExports: new[] { Export(PackageDescription("Pack.Age", servicable: false)) }); var lib = context.RuntimeLibraries.Single(); lib.Serviceable.Should().BeTrue(); } [Fact] public void TakesServicableFromPackageDescription() { var context = Build(runtimeExports: new[] { Export(PackageDescription("Pack.Age", servicable: true)) }); var lib = context.RuntimeLibraries.Single(); lib.Serviceable.Should().BeTrue(); } [Fact] public void FillsRuntimeLibraryProperties() { var context = Build(runtimeExports: new[] { Export( PackageDescription( "Pack.Age", servicable: true, hash: "Hash", version: new NuGetVersion(1, 2, 3), dependencies: new[] { new LibraryRange("System.Collections", new VersionRange(new NuGetVersion(2, 1, 2)), LibraryType.ReferenceAssembly, LibraryDependencyType.Default) }), resourceAssemblies: new[] { new LibraryResourceAssembly( new LibraryAsset("Dll", "en-US/Pack.Age.resources.dll", ""), "en-US" ) }, runtimeAssemblyGroups: new[] { new LibraryAssetGroup( new LibraryAsset("Dll", "lib/Pack.Age.dll", "")), new LibraryAssetGroup("win8-x64", new LibraryAsset("Dll", "win8-x64/Pack.Age.dll", "")) }, nativeLibraryGroups: new [] { new LibraryAssetGroup("win8-x64", new LibraryAsset("Dll", "win8-x64/Pack.Age.native.dll", "")) }), Export( ReferenceAssemblyDescription("System.Collections", version: new NuGetVersion(3, 3, 3)), runtimeAssemblyGroups: new[] { new LibraryAssetGroup( new LibraryAsset("Dll", "System.Collections.dll", "System.Collections.dll")) }) }); context.RuntimeLibraries.Should().HaveCount(2); var lib = context.RuntimeLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject; lib.Type.Should().Be("package"); lib.Serviceable.Should().BeTrue(); lib.Hash.Should().Be("sha512-Hash"); lib.Version.Should().Be("1.2.3"); lib.Dependencies.Should().OnlyContain(l => l.Name == "System.Collections" && l.Version == "3.3.3"); lib.ResourceAssemblies.Should().OnlyContain(l => l.Path == "en-US/Pack.Age.resources.dll" && l.Locale == "en-US"); lib.RuntimeAssemblyGroups.GetDefaultAssets().Should().OnlyContain(l => l == "lib/Pack.Age.dll"); lib.RuntimeAssemblyGroups.GetRuntimeAssets("win8-x64").Should().OnlyContain(l => l == "win8-x64/Pack.Age.dll"); lib.NativeLibraryGroups.GetRuntimeAssets("win8-x64").Should().OnlyContain(l => l == "win8-x64/Pack.Age.native.dll"); var asm = context.RuntimeLibraries.Should().Contain(l => l.Name == "System.Collections").Subject; asm.Type.Should().Be("referenceassembly"); asm.Version.Should().Be("3.3.3"); asm.Hash.Should().BeEmpty(); asm.Dependencies.Should().BeEmpty(); asm.RuntimeAssemblyGroups.GetDefaultAssets().Should().OnlyContain(l => l == "System.Collections.dll"); } [Fact] public void FiltersDuplicatedDependencies() { var context = Build(runtimeExports: new[] { Export(PackageDescription("Pack.Age", dependencies: new[] { new LibraryRange("System.Collections", new VersionRange(new NuGetVersion(2, 0, 0)), LibraryType.ReferenceAssembly, LibraryDependencyType.Default), new LibraryRange("System.Collections", new VersionRange(new NuGetVersion(2, 1, 2)), LibraryType.Package, LibraryDependencyType.Default) }) ), Export(ReferenceAssemblyDescription("System.Collections", version: new NuGetVersion(2, 0, 0))) }); context.RuntimeLibraries.Should().HaveCount(2); var lib = context.RuntimeLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject; lib.Dependencies.Should().HaveCount(1); lib.Dependencies.Should().OnlyContain(l => l.Name == "System.Collections" && l.Version == "2.0.0"); } [Fact] public void FillsCompileLibraryProperties() { var context = Build(compilationExports: new[] { Export(PackageDescription("Pack.Age", servicable: true, hash: "Hash", version: new NuGetVersion(1, 2, 3), dependencies: new[] { new LibraryRange("System.Collections", new VersionRange(new NuGetVersion(2, 1, 2)), LibraryType.ReferenceAssembly, LibraryDependencyType.Default) }), compilationAssemblies: new[] { new LibraryAsset("Dll", "lib/Pack.Age.dll", ""), } ), Export(ReferenceAssemblyDescription("System.Collections", version: new NuGetVersion(3, 3, 3)), compilationAssemblies: new[] { new LibraryAsset("Dll", "", "System.Collections.dll"), }) }); context.CompileLibraries.Should().HaveCount(2); var lib = context.CompileLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject; lib.Type.Should().Be("package"); lib.Serviceable.Should().BeTrue(); lib.Hash.Should().Be("sha512-Hash"); lib.Version.Should().Be("1.2.3"); lib.Dependencies.Should().OnlyContain(l => l.Name == "System.Collections" && l.Version == "3.3.3"); lib.Assemblies.Should().OnlyContain(a => a == "lib/Pack.Age.dll"); var asm = context.CompileLibraries.Should().Contain(l => l.Name == "System.Collections").Subject; asm.Type.Should().Be("referenceassembly"); asm.Version.Should().Be("3.3.3"); asm.Hash.Should().BeEmpty(); asm.Dependencies.Should().BeEmpty(); asm.Assemblies.Should().OnlyContain(a => a == "System.Collections.dll"); } [Fact] public void FillsResources() { var context = Build(runtimeExports: new[] { Export(PackageDescription("Pack.Age", version: new NuGetVersion(1, 2, 3)), resourceAssemblies: new [] { new LibraryResourceAssembly(new LibraryAsset("Dll", "resources/en-US/Pack.Age.dll", ""), "en-US") }) }); context.RuntimeLibraries.Should().HaveCount(1); var lib = context.RuntimeLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject; lib.ResourceAssemblies.Should().OnlyContain(l => l.Locale == "en-US" && l.Path == "resources/en-US/Pack.Age.dll"); } [Fact] public void ReferenceAssembliesPathRelativeToDefaultRoot() { var context = Build(compilationExports: new[] { Export(ReferenceAssemblyDescription("System.Collections", version: new NuGetVersion(3, 3, 3)), compilationAssemblies: new[] { new LibraryAsset("Dll", "", Path.Combine(_referenceAssembliesPath, "sub", "System.Collections.dll")) }) }); var asm = context.CompileLibraries.Should().Contain(l => l.Name == "System.Collections").Subject; asm.Assemblies.Should().OnlyContain(a => a == Path.Combine("sub", "System.Collections.dll")); } [Fact] public void SkipsBuildDependencies() { var context = Build(compilationExports: new[] { Export(PackageDescription("Pack.Age", dependencies: new[] { new LibraryRange("System.Collections", new VersionRange(new NuGetVersion(2, 1, 2)), LibraryType.ReferenceAssembly, LibraryDependencyType.Build) }) ), Export(ReferenceAssemblyDescription("System.Collections", version: new NuGetVersion(3, 3, 3))) }); var lib = context.CompileLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject; lib.Dependencies.Should().BeEmpty(); } [Fact] public void GeneratesRuntimeSignatureOutOfPackageNamesAndVersions() { var context = Build(runtimeExports: new[] { Export(PackageDescription("Pack.Age", new NuGetVersion(1, 2, 3))), Export(PackageDescription("Pack.Age", new NuGetVersion(1, 2, 3))), }); context.Target.RuntimeSignature.Should().Be("d0fc00006ed69e4aae80383dda08599a6892fd31"); } private LibraryExport Export( LibraryDescription description, IEnumerable compilationAssemblies = null, IEnumerable runtimeAssemblyGroups = null, IEnumerable nativeLibraryGroups = null, IEnumerable resourceAssemblies = null) { return LibraryExportBuilder.Create(description) .WithCompilationAssemblies(compilationAssemblies) .WithRuntimeAssemblyGroups(runtimeAssemblyGroups) .WithNativeLibraryGroups(nativeLibraryGroups) .WithResourceAssemblies(resourceAssemblies) .Build(); } private PackageDescription PackageDescription( string name = null, NuGetVersion version = null, string hash = null, IEnumerable dependencies = null, bool? servicable = null) { return new PackageDescription( "PATH", new LockFilePackageLibrary() { Files = new string[] { }, IsServiceable = servicable ?? false, Name = name ?? _defaultName, Version = version ?? _defaultVersion, Sha512 = hash ?? _defaultHash }, new LockFileTargetLibrary(), dependencies ?? Enumerable.Empty(), true, true); } private ProjectDescription ProjectDescription( string name = null, NuGetVersion version = null, IEnumerable dependencies = null) { return new ProjectDescription( new LibraryRange( name ?? _defaultName, new VersionRange(version ?? _defaultVersion), LibraryType.Project, LibraryDependencyType.Default ), new Project(), dependencies ?? Enumerable.Empty(), new TargetFrameworkInformation(), true); } private LibraryDescription ReferenceAssemblyDescription( string name = null, NuGetVersion version = null) { return new LibraryDescription( new LibraryIdentity( name ?? _defaultName, version ?? _defaultVersion, LibraryType.ReferenceAssembly), string.Empty, // Framework assemblies don't have hashes "PATH", Enumerable.Empty(), _defaultFramework, true, true); } } }