-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi-Targeting of engine extensions (.NET Framework only) #452
Conversation
using System.Reflection; | ||
using System.Text; | ||
using NUnit.Engine.Extensibility; | ||
using NUnit.Framework; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Significant changes in this file are mainly due to tests here split out into ExtensionSelectorTests
@@ -0,0 +1,77 @@ | |||
// *********************************************************************** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file has been moved and isn't entirely new - I guess GitHub hasn't noticed as there's also some changes.
@@ -0,0 +1,75 @@ | |||
// *********************************************************************** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functionality in this new class mostly extracted from ExtensionAssembly.cs - some changes to consider targetframework. Refactor was for test purposes.
{ | ||
if (!fromWildCard) | ||
throw; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional check as we now attempt to read target frameworks in this section, which can throw.
Tests are failing on mono-only, I'm going to guess some differences in assembly name reflection. Looking into it... |
13eb7ab
to
d37abbe
Compare
Issue is that our RuntimeFramework code currently believes Mono 5.10.0.160 can't support .NET 2.0 targeted things. I think I might have to deal with this as a separate issue... |
d37abbe
to
bd35170
Compare
Cool - an actual bug! 😄 Turns out Ready for review now! |
@rprouse - this is a non-trivial change, and could probably be merged post-release, if you'd prefer? I don't mind either way. 🙂 |
@@ -432,7 +446,7 @@ public void FindExtensionsInAssembly(ExtensionAssembly assembly) | |||
if (versionArg != null && new Version((string)versionArg) > ENGINE_VERSION) | |||
continue; | |||
|
|||
var node = new ExtensionNode(assembly.FilePath, type.FullName); | |||
var node = new ExtensionNode(assembly.FilePath, type.FullName, assemblyTargetFramework); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once we decide that we can load the assembly in the current runtime, why is the assemblyTargetFramework still needed in the extension node? Is it only so that the console runner can display it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, exactly. Info is only stored in ExtensionNode
to be accessible to runners.
"48cd57a6")] | ||
|
||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2,PublicKey=002400000480000094" + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did I miss where this is explained? Maybe a comment here would help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry - this is to allow using NSubstitute with internal types. It should have a comment, yes!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
I had to go back and re-read all the discussion, including my own comments, to try to remember why we were doing this! Restating what I think you are trying to accomplish in my own terms so you can tell me if I get it correctly... 😄
Where I am a bit confused is that I thought we already handled the first case. If you write an extension that targets say 4.5, then we don't load it unless we are running under 4.5 or higher. The existing extension service is supposed to take care of that and I'm pretty sure it once did. So, are we doing anything new here? Or are we just preparing for eventual .NET Core extensions? IOW, what is the use case that currently fails but will succeed under this PR? If that case is, for example, loading a 4.6 extension when running under 3.5, I'd like to figure out when it stopped working correctly - that is, ignoring the extension. At least I think it used to work that way! |
@CharliePoole - your understanding is correct. (Specifically, in my case right now, I want to write a net46 version of the vs-project-loader extension) What's definitely new, is that the engine will now purposefully choose the highest target framework version of an extension - previously, with two equal assembly-version extensions, it would have just taken the first it encountered. That allows us to package multiple versions of the same extension in the same NuGet package, and ensure the user always gets the 'best' version available. I don't believe there was any previous functionality at all to check if the current framework supports the target framework of the extension. As far as I can see, it would have crashed at the point it tries to load the assembly. I'd have thought that we previously haven't encountered that problem as all the extensions we look after are .NET 2.0. Whether any other extension authors have, I don't know. Of course, if we start distributing none .NET 2.0 extensions, then that situation becomes much more likely, so I added in the check. 🙂 |
My memory (which could easily be wrong) was that we caught the exception (from 4.6 for example) logged the error and ignored the assembly without crashing. I had to deal with it even though we didn't have extensions > 2.0 because there could still be assemblies in the same directory that targeted a higher version. In fact, at one point, we threw if there was a non-assembly in the same directory! However, this all may have been before we started using Mono.Cecil and you obviously have to deal with what we now do, not with what we used to do. In any case, selecting the highest loadable version is a new thing that I hadn't thought about. BTW, I assume you are dealing with the identical assembly being found twice? It's possible to set up connected |
Ah. So if there's an exception at the point of examining an assembly - then yes, we would catch and continue (or error, circumstance depending) - adding that was my first ever contribution to NUnit, in fact! 😄 I had thought however that it would be possible to examine an assembly targeting a higher framework without crashing...but I'm not sure. The particular case I fixed was an exception on examining an unmanaged dll.
I didn't change existing functionality here - if there's no differentiating factors two assemblies, the engine chooses the first it encounters. |
Then it all makes sense to me. Nice work! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks Great now!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good...
This is some unfinished work from a few months back - excuse it being a bit random! Fixes #394 as far as is currently possible - if we're considering implementing extension functionality for the .NET Standard engine as a whole separate issue!
The engine will now prioritise the extension with the highest target framework - when two of the same type are made available. Preference order in determining which extension version to load is now:
In the short term, this unblocks nunit/vs-project-loader#25 - something I might have a look at soon. In the longer-term - this is hopefully the foundations for resolving extensions made available for different platforms.
Again, I recommend review commit-by-commit - I'll add in a few line-comments shortly on some bits worth highlighting. The changes are less significant then they look - I did an amount of refactoring so we could test this better, as I imagine this functionality may become more complex over time!