Skip to content

Commit

Permalink
Merge pull request #848 from nunit/issue-844
Browse files Browse the repository at this point in the history
Fix extension loading for extensions which target multiple runtimes
  • Loading branch information
ChrisMaddock authored Jan 16, 2021
2 parents ada58b9 + bd0d308 commit 584f0f7
Showing 1 changed file with 103 additions and 98 deletions.
201 changes: 103 additions & 98 deletions src/NUnitEngine/nunit.engine.core/Services/ExtensionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -352,43 +352,46 @@ private void ProcessAddinsFile(DirectoryInfo baseDir, string fileName, bool from

private void ProcessCandidateAssembly(string filePath, bool fromWildCard)
{
if (!Visited(filePath))
if (Visited(filePath))
return;

Visit(filePath);

try
{
Visit(filePath);
var candidate = new ExtensionAssembly(filePath, fromWildCard);

if (!CanLoadTargetFramework(Assembly.GetEntryAssembly(), candidate))
return;

try
for (var i = 0; i < _assemblies.Count; i++)
{
var candidate = new ExtensionAssembly(filePath, fromWildCard);
var assembly = _assemblies[i];

for (int i = 0; i < _assemblies.Count; i++)
if (candidate.IsDuplicateOf(assembly))
{
var assembly = _assemblies[i];
if (candidate.IsBetterVersionOf(assembly))
_assemblies[i] = candidate;

if (candidate.IsDuplicateOf(assembly))
{
if (candidate.IsBetterVersionOf(assembly))
_assemblies[i] = candidate;

return;
}
return;
}

_assemblies.Add(candidate);
}
catch (BadImageFormatException e)
{
if (!fromWildCard)
throw new NUnitEngineException(String.Format("Specified extension {0} could not be read", filePath), e);
}
catch (NUnitEngineException)
{
if (!fromWildCard)
throw;
}

_assemblies.Add(candidate);
}
catch (BadImageFormatException e)
{
if (!fromWildCard)
throw new NUnitEngineException($"Specified extension {filePath} could not be read", e);
}
catch (NUnitEngineException)
{
if (!fromWildCard)
throw;
}
}

private Dictionary<string, object> _visited = new Dictionary<string, object>();
private readonly Dictionary<string, object> _visited = new Dictionary<string, object>();

private bool Visited(string filePath)
{
Expand All @@ -407,101 +410,103 @@ private void Visit(string filePath)
/// </summary>
internal void FindExtensionsInAssembly(ExtensionAssembly assembly)
{
log.Info("Scanning {0} assembly for Extensions", assembly.FilePath);
log.Info($"Scanning {assembly.FilePath} for Extensions");

if (CanLoadTargetFramework(Assembly.GetEntryAssembly(), assembly))
if (!CanLoadTargetFramework(Assembly.GetEntryAssembly(), assembly))
{
log.Info($"{assembly.FilePath} cannot be loaded on this runtime");
return;
}

IRuntimeFramework assemblyTargetFramework = null;
IRuntimeFramework assemblyTargetFramework = null;
#if NETFRAMEWORK
var currentFramework = RuntimeFramework.CurrentFramework;
assemblyTargetFramework = assembly.TargetFramework;
if (!currentFramework.CanLoad(assemblyTargetFramework))
var currentFramework = RuntimeFramework.CurrentFramework;
assemblyTargetFramework = assembly.TargetFramework;
if (!currentFramework.CanLoad(assemblyTargetFramework))
{
//Temp fix: Prevent crash in agent if an extension is used by the main engine, which targets a framework that can not be loaded on a particular agent
// https://github.com/nunit/nunit-console/issues/757
//Long-term fix is to not search for extensions within the agent, see https://github.com/nunit/nunit-console/issues/760
if (_isRunningOnAgent)
{
//Temp fix: Prevent crash in agent if an extension is used by the main engine, which targets a framework that can not be loaded on a particular agent
// https://github.com/nunit/nunit-console/issues/757
//Long-term fix is to not search for extensions within the agent, see https://github.com/nunit/nunit-console/issues/760
if (_isRunningOnAgent)
{
return;
}
return;
}

if (!assembly.FromWildCard)
{
throw new NUnitEngineException($"Extension {assembly.FilePath} targets {assemblyTargetFramework.DisplayName}, which is not available.");
}
else
{
log.Info($"Assembly {assembly.FilePath} targets {assemblyTargetFramework.DisplayName}, which is not available. Assembly found via wildcard.");
return;
}
if (!assembly.FromWildCard)
{
throw new NUnitEngineException($"Extension {assembly.FilePath} targets {assemblyTargetFramework.DisplayName}, which is not available.");
}
else
{
log.Info($"Assembly {assembly.FilePath} targets {assemblyTargetFramework.DisplayName}, which is not available. Assembly found via wildcard.");
return;
}
}
#endif

foreach (var type in assembly.MainModule.GetTypes())
{
CustomAttribute extensionAttr = type.GetAttribute("NUnit.Engine.Extensibility.ExtensionAttribute");
foreach (var type in assembly.MainModule.GetTypes())
{
CustomAttribute extensionAttr = type.GetAttribute("NUnit.Engine.Extensibility.ExtensionAttribute");

if (extensionAttr == null)
continue;
if (extensionAttr == null)
continue;

object versionArg = extensionAttr.GetNamedArgument("EngineVersion");
if (versionArg != null && new Version((string)versionArg) > ENGINE_VERSION)
continue;
object versionArg = extensionAttr.GetNamedArgument("EngineVersion");
if (versionArg != null && new Version((string)versionArg) > ENGINE_VERSION)
continue;

var node = new ExtensionNode(assembly.FilePath, assembly.AssemblyVersion, type.FullName, assemblyTargetFramework);
node.Path = extensionAttr.GetNamedArgument("Path") as string;
node.Description = extensionAttr.GetNamedArgument("Description") as string;
var node = new ExtensionNode(assembly.FilePath, assembly.AssemblyVersion, type.FullName, assemblyTargetFramework);
node.Path = extensionAttr.GetNamedArgument("Path") as string;
node.Description = extensionAttr.GetNamedArgument("Description") as string;

object enabledArg = extensionAttr.GetNamedArgument("Enabled");
node.Enabled = enabledArg != null
? (bool)enabledArg : true;
object enabledArg = extensionAttr.GetNamedArgument("Enabled");
node.Enabled = enabledArg != null
? (bool)enabledArg : true;

log.Info(" Found ExtensionAttribute on Type " + type.Name);
log.Info(" Found ExtensionAttribute on Type " + type.Name);

foreach (var attr in type.GetAttributes("NUnit.Engine.Extensibility.ExtensionPropertyAttribute"))
foreach (var attr in type.GetAttributes("NUnit.Engine.Extensibility.ExtensionPropertyAttribute"))
{
string name = attr.ConstructorArguments[0].Value as string;
string value = attr.ConstructorArguments[1].Value as string;

if (name != null && value != null)
{
string name = attr.ConstructorArguments[0].Value as string;
string value = attr.ConstructorArguments[1].Value as string;

if (name != null && value != null)
{
node.AddProperty(name, value);
log.Info(" ExtensionProperty {0} = {1}", name, value);
}
node.AddProperty(name, value);
log.Info(" ExtensionProperty {0} = {1}", name, value);
}
}

_extensions.Add(node);
_extensions.Add(node);

ExtensionPoint ep;
if (node.Path == null)
ExtensionPoint ep;
if (node.Path == null)
{
ep = DeduceExtensionPointFromType(type);
if (ep == null)
{
ep = DeduceExtensionPointFromType(type);
if (ep == null)
{
string msg = string.Format(
"Unable to deduce ExtensionPoint for Type {0}. Specify Path on ExtensionAttribute to resolve.",
type.FullName);
throw new NUnitEngineException(msg);
}

node.Path = ep.Path;
string msg = string.Format(
"Unable to deduce ExtensionPoint for Type {0}. Specify Path on ExtensionAttribute to resolve.",
type.FullName);
throw new NUnitEngineException(msg);
}
else

node.Path = ep.Path;
}
else
{
ep = GetExtensionPoint(node.Path);
if (ep == null)
{
ep = GetExtensionPoint(node.Path);
if (ep == null)
{
string msg = string.Format(
"Unable to locate ExtensionPoint for Type {0}. The Path {1} cannot be found.",
type.FullName,
node.Path);
throw new NUnitEngineException(msg);
}
string msg = string.Format(
"Unable to locate ExtensionPoint for Type {0}. The Path {1} cannot be found.",
type.FullName,
node.Path);
throw new NUnitEngineException(msg);
}

ep.Install(node);
}

ep.Install(node);
}
}

Expand Down Expand Up @@ -540,4 +545,4 @@ internal static bool CanLoadTargetFramework(Assembly runnerAsm, ExtensionAssembl
}
}
}
#endif
#endif

0 comments on commit 584f0f7

Please sign in to comment.