-
Notifications
You must be signed in to change notification settings - Fork 153
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
Should --Explore Error On "Invalid" Assemblies? #794
Comments
Interesting question about what the behaviour of explore should be - especially when we have other open issues like #658 to make it more strict! My initial thoughts - That's how I currently see explore, just to find the tests in the explicit list of assemblies you're passing in. What you're trying to do seems to me to be a bit of a different use-case, more of a 'discover'? I would say there's another argument as to whether crashing in this way is the correct behaviour, of if we should instead suppress the exception here, and create a @nunit/engine-team - anyone any thoughts? |
@aolszowka - what's you're actual usecase, sorry - are you intending to use the console, or are you planning to use the engine itself? |
Thinking more about this, this particular crash I'm pretty sure should definitely be reworked to use That fixes the engine - the question then is how the Console should behave. Given I don't know how the console behaves in this situation currently however, that's a difficult one to reason about! The crash is triggered by this, which shouldn't really be here anyway. 😬 We'll need to have a think about how we're going to mark up sub-packages as invalid, I think. nunit-console/src/NUnitEngine/nunit.engine/Runners/MasterTestRunner.cs Lines 241 to 247 in 0d037ca
|
At a certain level, this is understandable. That is, if you tell the console to explore something that is not a test assembly, or possibly not even an assembly, then an error should be expected. It's always been a basic (maybe unstated) principle of NUnit that it tries to do exactly what you tell it to do rather than assuming you meant something else. Of course, if the OP's error means that the program crashes, that's a bug. We should error by giving an error message and then exiting with a recognizable return code. The case where you have a bunch of dlls in a directory and don't actually know which of them have tests is another matter. Neither the console runner nor the engine has a feature to do that, so the user is forced to either pass in all the assemblies or to determine outside of NUnit which ones are test assemblies. There is an option to ignore non-test assemblies, so maybe there should be an option to ignore anything that can't be loaded. If we wanted to take it a step further, we could have a feature that allows exploring an entire set of assemblies using a pattern. |
Thank you all for the responses let me start by responding oldest to newest:
Basically we have a common bin output location in which hundreds of DLL's end up, some managed, some unmanaged, and we are asking the question "Which of these have tests?". This list is then sent to NUnit Console to execute. Right now today we have an in house MSBuild task that does this, the jist of which is: /// <summary>
/// Identify if the given assembly is a unit test.
/// </summary>
/// <returns>
/// <c>true</c> if the given assembly is a unit test; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// Returns <c>true</c> only if the assembly contains a reference to
/// NUnit.Framework and does not contain InteractiveTest in its
/// Assembly Name (NOT the filename!).
/// </remarks>
private bool _IsAUnitTestAssembly(string targetAssembly)
{
bool isATestAssembly = false;
// We need to load each assembly into its own AppDomain this
// allows us to unload the assembly once we're done. Also this
// avoids any potential issues with assemblies that may have
// already been loaded into our AppDomain
AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationBase = Environment.CurrentDirectory;
AppDomain ad = AppDomain.CreateDomain("currentTargetAssembly" + targetAssembly, null, ads);
// This is a Remote Object!
DependencyScannerRemote dsi =
(DependencyScannerRemote)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName,
typeof(DependencyScannerRemote).FullName);
if (dsi.IsManagedAssembly(targetAssembly))
{
string assemblyName = dsi.GetAssemblyName(targetAssembly);
if (assemblyName.Contains(INTERACTIVE_TEST_IDENTIFIER))
{
Log.LogMessage(MessageImportance.Normal, Resources.GetUnitTestAssembliesSkippingInteractiveTest, assemblyName);
}
else
{
IEnumerable<string> assemblyReferences = dsi.GetReferencedAssemblies(targetAssembly);
isATestAssembly = assemblyReferences.Any(current => current.ToLower().StartsWith(NUNIT_FRAMEWORK));
}
}
else
{
if (this.OnlyWarnOnFailureToLoadAssembly)
{
Log.LogWarning(Resources.GetUnitTestAssembliesNotManagedAssembly, targetAssembly);
}
else
{
Log.LogError(Resources.GetUnitTestAssembliesNotManagedAssembly, targetAssembly);
}
}
// Unload this AppDomain
AppDomain.Unload(ad);
return isATestAssembly;
} As you can see it does a bit more than just see if the Assembly contains a reference to
I think this is the answer to the question. Totally fair and I realize its a design choice :) just wanted to make that clear. I feel OK closing this issue without further discussion. Thank you again for taking the time to answer and thank you for NUnit! |
Here's what got me thinking about a feature... If you run *. dll on some (most? many?) Linux shells, the runner gets passed a list of assemblies, doesn't it? If that's the case, perhaps we should deal with it, recognizing that the user may not have literally typed in the invalid file name. |
FWIW I generated the "Bad" listing by doing something similar in Windows: echo --explore> Assemblies.txt
dir /b /s *.dll >> Assemblies.txt
nunit3-console.exe @Assemblies.txt Not as slick as Totally aware that I was subverting our in-house tooling to filter to just NUnit Test Assemblies, I was just looking for a quick one off rather than opening up our cumbersome tooling (and this is coming from the guy who wrote that tooling!) |
@aolszowka The key difference, as I see it, is that the naive user may assume that NUnit runner itself is supporting Now you could run |
Ok, let's draw some conclusions out of this. I think we all agree the engine shouldn't crash! I've created #798 to cover that. I think we also agree that this isn't really what @aolszowka - there's one bit missing in my mental model of your process - how are you planning to get from the output of As I say, I don't think this was ever intended as functionality, but if we accept that the crash shouldn't happen, then I can't think why it should be possible. @CharliePoole - can you think of anything I'm missing? |
@ChrisMaddock I don't think you're missing anything. :-) An important distinction between @aolszowka 's experiment using a response file is that the runner doesn't know how that file was generated. In the second execution of If we had an option like |
@ChrisMaddock I think @CharliePoole hit it right on the head; I intend to pipe the output of --explore more or less into a response file to perform execution on. The original use case was I was just trying to get a listing of all NUnit Tests that had a category of "Smoke". We are reevaluating some of these "Smoke" tests for dubious/heavy tests as part of a rewrite of our CI. I would have used --explore to get the filtered tests and then piped to another post processing program (to filter it down even more) and then piped it right back into nunit3-console to get the output. Maybe think of it as a second layer of filtering that has our business specific logic applied to it? I am about to head offline for about a week and a half and I apologize for not getting back to you immediately, but I promise to do so when I get back. Thank you for all your work on NUnit and Open Source! |
Ahh ok! So hang on - That bits important, as trying to execute an assembly with no tests counts as invalid, and gives a non-zero exit code in the NUnit Console. (Regardless of whether you use To make this work, I think you would need some sort of way to turn your list of tests into a list of valid assemblies. I don't know if you test names are standardised enough to be able to script that? A second, more hair-brained idea might be to implement an
No problem - hope you have a good break! 👋 |
Oh, I'm really overthinking this. I forgot that --explore can take an output spec as a parameter. I'd recommend writing a simple Result Writer Extension, which you can use with --explore to output a list of valid assembly names. That's much cleaner than the rubbish I was sprouting in my last post, and actually quite a nice use of some more niche functionality! 😄 TestCaseResultWriter is a good example of the sort of thing you’d need to do: https://github.com/nunit/nunit-console/blob/master/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs |
Yeah, I think we have jumped back and forth between explore creating test input and the user creating a list of assemblies. My thinking on the latter was that the list, generated simply enough on the command-line would go in a response file. The hypothetical --skipxxxx option would tell NUnit "Don't be surprised if some of these files can't be loaded." I think that's the problem we started out with here. |
Hey back, and yeah I think @CharliePoole is on the right track with what I am thinking. Realistically though it's a more "nice to have" rather than a serious gap of any sort. A good rule of thumb might be the willingness to provide a patch that has the changes, at this time I probably wouldn't chime up too loud, but would throw it on a "nice to have" list. I am not sure where these types of issues go, and I am totally OK with closing this with a non-actionable status. |
Thanks for starting the conversation @aolszowka! #798 now covers the crash, and I've just created #827 to cover the potential change in behaviour, which I personally think would be a good one! 🙂 |
Reproduce With The Following:
Yields:
Obviously because this is a completely invalid DLL.
Our use case is a little more exotic than this, basically we have a common bin output location in which hundreds of DLL's end up, some managed, some unmanaged, and we are asking the question "Which of these have tests?".
The error in question comes from:
nunit-console/src/NUnitEngine/nunit.engine.core/Internal/TargetFrameworkHelper.cs
Line 44 in 0958996
We have similar code in our code base, however we simply ignore the load failure.
The question is more one of design, should this be the expected behavior? Or should
--explore
be "Best Effort"?The text was updated successfully, but these errors were encountered: