#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using HeuristicLab.PluginInfrastructure; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace HeuristicLab.Tests { [TestClass] public class PluginDependenciesTest { private static Dictionary loadedPlugins; private static Dictionary pluginNames; private static Dictionary pluginFilesToPluginLookup = new Dictionary(); // Use ClassInitialize to run code before running the first test in the class [ClassInitialize] public static void MyClassInitialize(TestContext testContext) { loadedPlugins = PluginLoader.Assemblies.Where(PluginLoader.IsPluginAssembly).ToDictionary(a => a, GetPluginFromAssembly); pluginNames = loadedPlugins.ToDictionary(a => a.Key.GetName().FullName, a => GetPluginName(a.Value)); foreach (Assembly pluginAssembly in loadedPlugins.Keys) { Type pluginType = GetPluginFromAssembly(pluginAssembly); var pluginFileAttributes = Attribute.GetCustomAttributes(pluginType, false).OfType(); foreach (var pluginFileAttribute in pluginFileAttributes) { string fillNameWithoutExtension = Path.GetFileNameWithoutExtension(pluginFileAttribute.FileName); pluginFilesToPluginLookup.Add(fillNameWithoutExtension, pluginAssembly); } } } [TestMethod] [TestCategory("General")] [TestCategory("Essential")] [TestProperty("Time", "short")] public void CheckReferenceAssembliesForPluginDependencies() { StringBuilder errorMessage = new StringBuilder(); foreach (Assembly pluginAssembly in loadedPlugins.Keys) { Type plugin = loadedPlugins[pluginAssembly]; var pluginFiles = new HashSet(Attribute.GetCustomAttributes(plugin, false) .OfType().Where(pf => pf.FileType == PluginFileType.Assembly).Select(pf => pf.FileName)); var pluginAssemblies = PluginLoader.Assemblies.Where(a => pluginFiles.Contains(Path.GetFileName(a.Location))).ToList(); var referencedAssemblies = pluginAssemblies.SelectMany(a => a.GetReferencedAssemblies()).ToList(); Dictionary pluginDependencies = Attribute.GetCustomAttributes(plugin, false).OfType().ToDictionary(a => a.Dependency); foreach (AssemblyName referencedAssemblyName in referencedAssemblies) { if (IsPluginAssemblyName(referencedAssemblyName)) { if (!pluginDependencies.ContainsKey(pluginNames[referencedAssemblyName.FullName])) errorMessage.AppendLine("Missing dependency in plugin " + plugin + " to referenced plugin " + pluginNames[referencedAssemblyName.FullName] + "."); } else { //no plugin assembly => test if the assembly is delivered by another plugin if (pluginFilesToPluginLookup.ContainsKey(referencedAssemblyName.Name)) { string containingPluginFullName = pluginFilesToPluginLookup[referencedAssemblyName.Name].FullName; if (containingPluginFullName != pluginAssembly.FullName && !pluginDependencies.ContainsKey(pluginNames[containingPluginFullName])) errorMessage.AppendLine("Missing dependency in plugin " + plugin + " to plugin " + pluginNames[containingPluginFullName] + " due to a reference to " + referencedAssemblyName.FullName + "."); } } } } Assert.IsTrue(errorMessage.Length == 0, errorMessage.ToString()); } [TestMethod] [TestCategory("General")] [TestCategory("Essential")] [TestProperty("Time", "short")] public void CheckPluginDependenciesForReferencedAssemblies() { StringBuilder errorMessage = new StringBuilder(); foreach (Assembly pluginAssembly in loadedPlugins.Keys) { Type plugin = loadedPlugins[pluginAssembly]; Dictionary pluginDependencies = Attribute.GetCustomAttributes(plugin, false).OfType().ToDictionary(a => a, a => a.Dependency); var pluginFiles = new HashSet(Attribute.GetCustomAttributes(plugin, false) .OfType().Where(pf => pf.FileType == PluginFileType.Assembly).Select(pf => pf.FileName)); var pluginAssemblies = PluginLoader.Assemblies.Where(a => pluginFiles.Contains(Path.GetFileName(a.Location))).ToList(); var referencedAssemblies = pluginAssemblies.SelectMany(a => a.GetReferencedAssemblies()).ToList(); foreach (PluginDependencyAttribute attribute in pluginDependencies.Keys) { string pluginDependencyName = pluginDependencies[attribute]; if (pluginDependencyName == "HeuristicLab.MathJax") continue; //is never referenced as this plugin contains HTML files if (pluginDependencyName == "HeuristicLab.MatlabConnector") continue; //the matlab connector is loaded dynamically and hence not referenced by the dll var referencedPluginAssemblies = referencedAssemblies.Where(IsPluginAssemblyName); if (referencedPluginAssemblies.Any(a => pluginNames[a.FullName] == pluginDependencyName)) continue; var referencedNonPluginAssemblies = referencedAssemblies.Where(a => !IsPluginAssemblyName(a)); bool found = (from referencedNonPluginAssembly in referencedNonPluginAssemblies select referencedNonPluginAssembly.Name into assemblyName where pluginFilesToPluginLookup.ContainsKey(assemblyName) select GetPluginFromAssembly(pluginFilesToPluginLookup[assemblyName]) into pluginType select GetPluginName(pluginType)).Any(pluginName => pluginName == pluginDependencyName); if (!found) errorMessage.AppendLine("Unnecessary plugin dependency in " + GetPluginName(plugin) + " to " + pluginDependencyName + "."); } } Assert.IsTrue(errorMessage.Length == 0, errorMessage.ToString()); } private static Type GetPluginFromAssembly(Assembly assembly) { return assembly.GetExportedTypes().FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface); } private static string GetPluginName(Type plugin) { string name = string.Empty; PluginAttribute pluginAttribute = (PluginAttribute)Attribute.GetCustomAttribute(plugin, typeof(PluginAttribute)); if (pluginAttribute != null) name = pluginAttribute.Name; return name; } private static bool IsPluginAssemblyName(AssemblyName assemblyName) { return pluginNames.ContainsKey(assemblyName.FullName); } } }