using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Prise.AssemblyScanning; using Prise.Tests.Plugins; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; namespace Prise.Tests.AssemblyScanning { [TestClass] public class DefaultNugetPackageAssemblyScannerTests : TestBase { [TestMethod] public void Ctor_No_NugetUtilitiesFactory_Throws_ArgumentNullException() { var exception = Assert.ThrowsException(() => new DefaultNugetPackageAssemblyScanner((s) => null, () => null, null)); exception.Message.Contains("nugetPackageUtilities"); } [TestMethod] public async Task Scan_No_Options_Throws_ArgumentNullException() { var scanner = new DefaultNugetPackageAssemblyScanner((s) => null, () => null, () => null); await Assert.ThrowsExceptionAsync(() => scanner.Scan(null)); } [TestMethod] public async Task Scan_No_StartingPath_Throws_ArgumentException() { var scanner = new DefaultNugetPackageAssemblyScanner((s) => null, () => null, () => null); await Assert.ThrowsExceptionAsync(() => scanner.Scan(new AssemblyScannerOptions { PluginType = typeof(ITestPlugin) })); } [TestMethod] public async Task Scan_No_PluginType_Throws_ArgumentException() { var scanner = new DefaultNugetPackageAssemblyScanner((s) => null, () => null, () => null); await Assert.ThrowsExceptionAsync(() => scanner.Scan(new AssemblyScannerOptions { StartingPath = "/home/maarten" })); } [TestMethod] public async Task Scan_Unrooted_Path_Throws_AssemblyScanningException() { var scanner = new DefaultNugetPackageAssemblyScanner((s) => null, () => null, () => null); await Assert.ThrowsExceptionAsync(() => scanner.Scan(new AssemblyScannerOptions { StartingPath = "../home/maarten", PluginType = typeof(ITestPlugin) })); } [TestMethod] public async Task Scan_No_Nugets_Throws_AssemblyScanningException() { var startingPath = "/home/maarten"; var nugetUtilities = this.mockRepository.Create(); nugetUtilities.Setup(n => n.FindAllNugetPackagesFiles(startingPath)).Returns(Enumerable.Empty()); var scanner = new DefaultNugetPackageAssemblyScanner((s) => null, () => null, () => nugetUtilities.Object); await Assert.ThrowsExceptionAsync(() => scanner.Scan(new AssemblyScannerOptions { StartingPath = startingPath, PluginType = typeof(ITestPlugin) })); } [TestMethod] public async Task Compressed_NugetPackage_Must_Be_UnCompressed() { var package = "Prise.Plugin.Package.1.0.0.nupkg"; var startingPath = "/home/maarten"; var nugetUtilities = this.mockRepository.Create(); var metadataLoadContext = this.mockRepository.Create(); var assemblyShim = this.mockRepository.Create(); var directoryTraverser = this.mockRepository.Create(); var actualNugetUtilities = new DefaultNugetPackageUtilities(); nugetUtilities.Setup(n => n.FindAllNugetPackagesFiles(startingPath)).Returns(new[] { package }); // Call actual implementation here nugetUtilities.Setup(n => n.GetVersionFromPackageFile(It.IsAny())).Returns((s) => actualNugetUtilities.GetVersionFromPackageFile(s)); // Call actual implementation here nugetUtilities.Setup(n => n.GetPackageName(It.IsAny())).Returns((s) => actualNugetUtilities.GetPackageName(s)); nugetUtilities.Setup(n => n.HasAlreadyBeenExtracted(It.IsAny())).Returns(false); nugetUtilities.Setup(n => n.UnCompressNugetPackage(It.IsAny(), It.IsAny())).Verifiable(); directoryTraverser.Setup(d => d.TraverseDirectories($"{startingPath}/_extracted")).Returns(new[] { "pathy/mcpathface" }); directoryTraverser.Setup(d => d.TraverseFiles(It.IsAny(), It.IsAny>())).Returns(new[] { "filey.mcfile.face" }); var contract = TestableTypeBuilder.New() .WithName("IMyTestType") .WithNamespace("Test.Type") .Build(); var pluginAttributeTypedValue = new CustomAttributeTypedArgument(contract); var pluginAttribute = new TestableAttribute { _AttributeType = typeof(Prise.Plugin.PluginAttribute), _NamedArguments = new[]{new CustomAttributeNamedArgument(new TestableMemberInfo{ _Name = "PluginType" },pluginAttributeTypedValue)} }; var testableType = TestableTypeBuilder.New() .WithCustomAttributes(pluginAttribute) .WithName("MyTestType") .WithNamespace("Test.Type") .Build(); assemblyShim.Setup(a => a.Types).Returns(new[] { testableType }); metadataLoadContext.Setup(c => c.LoadFromAssemblyName(It.IsAny())).Returns(assemblyShim.Object); var scanner = new DefaultNugetPackageAssemblyScanner( (s) => metadataLoadContext.Object, () => directoryTraverser.Object, () => nugetUtilities.Object ); var types = await scanner.Scan(new AssemblyScannerOptions { StartingPath = startingPath, PluginType = contract }); var result = types.FirstOrDefault(); Assert.IsNotNull(result); Assert.AreEqual("MyTestType", result.PluginType.Name); Assert.AreEqual("Test.Type", result.PluginType.Namespace); } [TestMethod] public async Task UnCompressed_NugetPackage_Does_Nothing() { var package = "Prise.Plugin.Package.1.0.0.nupkg"; var startingPath = "/home/maarten"; var nugetUtilities = this.mockRepository.Create(); var metadataLoadContext = this.mockRepository.Create(); var assemblyShim = this.mockRepository.Create(); var directoryTraverser = this.mockRepository.Create(); var actualNugetUtilities = new DefaultNugetPackageUtilities(); nugetUtilities.Setup(n => n.FindAllNugetPackagesFiles(startingPath)).Returns(new[] { package }); // Call actual implementation here nugetUtilities.Setup(n => n.GetVersionFromPackageFile(It.IsAny())).Returns((s) => actualNugetUtilities.GetVersionFromPackageFile(s)); // Call actual implementation here nugetUtilities.Setup(n => n.GetPackageName(It.IsAny())).Returns((s) => actualNugetUtilities.GetPackageName(s)); nugetUtilities.Setup(n => n.HasAlreadyBeenExtracted(It.IsAny())).Returns(true); directoryTraverser.Setup(d => d.TraverseDirectories($"{startingPath}/_extracted")).Returns(new[] { "pathy/mcpathface" }); directoryTraverser.Setup(d => d.TraverseFiles(It.IsAny(), It.IsAny>())).Returns(new[] { "filey.mcfile.face" }); var contract = TestableTypeBuilder.New() .WithName("IMyTestType") .WithNamespace("Test.Type") .Build(); var pluginAttributeTypedValue = new CustomAttributeTypedArgument(contract); var pluginAttribute = new TestableAttribute { _AttributeType = typeof(Prise.Plugin.PluginAttribute), _NamedArguments = new[]{new CustomAttributeNamedArgument(new TestableMemberInfo{ _Name = "PluginType" },pluginAttributeTypedValue)} }; var testableType = TestableTypeBuilder.New() .WithCustomAttributes(pluginAttribute) .WithName("MyTestType") .WithNamespace("Test.Type") .Build(); assemblyShim.Setup(a => a.Types).Returns(new[] { testableType }); metadataLoadContext.Setup(c => c.LoadFromAssemblyName(It.IsAny())).Returns(assemblyShim.Object); var scanner = new DefaultNugetPackageAssemblyScanner( (s) => metadataLoadContext.Object, () => directoryTraverser.Object, () => nugetUtilities.Object ); var types = await scanner.Scan(new AssemblyScannerOptions { StartingPath = startingPath, PluginType = contract }); var result = types.FirstOrDefault(); Assert.IsNotNull(result); Assert.AreEqual("MyTestType", result.PluginType.Name); Assert.AreEqual("Test.Type", result.PluginType.Namespace); } [TestMethod] public async Task Multiple_Versions_With_Current_Already_Extracted_Only_Extracts_Older() { var currentVersion = new Version("1.1.0"); var package1 = "Prise.Plugin.Package.1.0.0.nupkg"; var package2 = "Prise.Plugin.Package.1.1.0.nupkg"; var startingPath = "/home/maarten"; var nugetUtilities = this.mockRepository.Create(); var metadataLoadContext = this.mockRepository.Create(); var assemblyShim = this.mockRepository.Create(); var directoryTraverser = this.mockRepository.Create(); var actualNugetUtilities = new DefaultNugetPackageUtilities(); nugetUtilities.Setup(n => n.FindAllNugetPackagesFiles(startingPath)).Returns(new[] { package1, package2 }); // Call actual implementation here nugetUtilities.Setup(n => n.GetVersionFromPackageFile(It.IsAny())).Returns((s) => actualNugetUtilities.GetVersionFromPackageFile(s)); // Call actual implementation here nugetUtilities.Setup(n => n.GetPackageName(It.IsAny())).Returns((s) => actualNugetUtilities.GetPackageName(s)); nugetUtilities.Setup(n => n.HasAlreadyBeenExtracted($"{startingPath}/_extracted/Prise.Plugin.Package/1.0.0")).Returns(false); nugetUtilities.Setup(n => n.HasAlreadyBeenExtracted($"{startingPath}/_extracted/Prise.Plugin.Package/1.1.0")).Returns(true); nugetUtilities.Setup(n => n.UnCompressNugetPackage(It.IsAny(), $"{startingPath}/_extracted/Prise.Plugin.Package/1.0.0")).Verifiable(); directoryTraverser.Setup(d => d.TraverseDirectories($"{startingPath}/_extracted")).Returns(new[] { "pathy/mcpathface" }); directoryTraverser.Setup(d => d.TraverseFiles(It.IsAny(), It.IsAny>())).Returns(new[] { "filey.mcfile.face" }); var contract = TestableTypeBuilder.New() .WithName("IMyTestType") .WithNamespace("Test.Type") .Build(); var pluginAttributeTypedValue = new CustomAttributeTypedArgument(contract); var pluginAttribute = new TestableAttribute { _AttributeType = typeof(Prise.Plugin.PluginAttribute), _NamedArguments = new[]{new CustomAttributeNamedArgument(new TestableMemberInfo{ _Name = "PluginType" },pluginAttributeTypedValue)} }; var testableType = TestableTypeBuilder.New() .WithCustomAttributes(pluginAttribute) .WithName("MyTestType") .WithNamespace("Test.Type") .Build(); assemblyShim.Setup(a => a.Types).Returns(new[] { testableType }); metadataLoadContext.Setup(c => c.LoadFromAssemblyName(It.IsAny())).Returns(assemblyShim.Object); var scanner = new DefaultNugetPackageAssemblyScanner( (s) => metadataLoadContext.Object, () => directoryTraverser.Object, () => nugetUtilities.Object ); var types = await scanner.Scan(new AssemblyScannerOptions { StartingPath = startingPath, PluginType = contract }); var result = types.FirstOrDefault(); Assert.IsNotNull(result); Assert.AreEqual("MyTestType", result.PluginType.Name); Assert.AreEqual("Test.Type", result.PluginType.Namespace); } [TestMethod] public async Task Scan_Succeeds() { var metadataLoadContext = this.mockRepository.Create(); var assemblyShim = this.mockRepository.Create(); var directoryTraverser = this.mockRepository.Create(); var scanner = new DefaultAssemblyScanner( (s) => metadataLoadContext.Object, () => directoryTraverser.Object ); directoryTraverser.Setup(d => d.TraverseDirectories(It.IsAny())).Returns(new[] { "pathy/mcpathface" }); directoryTraverser.Setup(d => d.TraverseFiles(It.IsAny(), It.IsAny>())).Returns(new[] { "filey.mcfile.face" }); var contract = TestableTypeBuilder.New() .WithName("IMyTestType") .WithNamespace("Test.Type") .Build(); var pluginAttributeTypedValue = new CustomAttributeTypedArgument(contract); var pluginAttribute = new TestableAttribute { _AttributeType = typeof(Prise.Plugin.PluginAttribute), _NamedArguments = new[]{new CustomAttributeNamedArgument(new TestableMemberInfo{ _Name = "PluginType" },pluginAttributeTypedValue)} }; var testableType = TestableTypeBuilder.New() .WithCustomAttributes(pluginAttribute) .WithName("MyTestType") .WithNamespace("Test.Type") .Build(); assemblyShim.Setup(a => a.Types).Returns(new[] { testableType }); metadataLoadContext.Setup(c => c.LoadFromAssemblyName(It.IsAny())).Returns(assemblyShim.Object); var types = await scanner.Scan(new AssemblyScannerOptions { StartingPath = "/home/maarten", PluginType = contract }); var result = types.FirstOrDefault(); Assert.IsNotNull(result); Assert.AreEqual("MyTestType", result.PluginType.Name); Assert.AreEqual("Test.Type", result.PluginType.Namespace); } } }