-
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
Test count in start-run report is always 0 #441
Comments
Some googling in the repository gave #146 and in particular the following #146 (comment) about why the count of test cases for the Note: we possibly have the same issue in the netstandard version, |
Well, that makes sense. However @CharliePoole 's comment:
Is only true by default. For example, there is a teamcity addin shipped with NUnit which produces special output while running tests, and I have a similar addin whose purpose is to provide progress information to a tool that's running nunit. For our particular test workloads we won't actually end up starting a lot of processes, but I certainly appreciate that it can be a problem in general. My PR adds an command line option. |
Just to be clear on the above: I need the test count in order to display progress. |
@cyanite Your point is a good one. If an extension wants a valid count then we should try to give it. A command-line option feels like a hack though. Can we allow an extension to indicate what it needs or directly ask for it instead? |
@CharliePoole I agree it's a bit hack-ish. Having the extension activate the load would be ideal, but I don't see how it's possible at the moment. The extension interfaces don't have a lot of methods. The one I am using is the ITestEventListener with just one method. Also, the call to Load (in my "hack") is all the way in the console-runner executable, which otherwise just calls Run directly. I feel a command line option, while not ideal, would still be useful. Maybe a longer, less ambiguous name. Otherwise we perhaps need something like an optional IConfiguration-something interface that extensions can implement. |
@cyanite thanks for the pull request, but I agree with @CharliePoole on this one, a command line option isn't the right way to go, so I closed that PR. An |
@rprouse, alright, then. I'll have to keep the changes in my local build since we need progress reports on our large suites. Also note that the PR fixes another crash issue (in Load). |
@rprouse Of course it would be easy to add a method/property "ShouldLoad" to ITestEventListener but that would break all existing extensions. Alternatively, a new callback-style interface could be optionally implemented, which would have a method where the test runner would be passed. But which test runner, then? Any ITestEventListeners are added at the MasterTestRunner level, but other extension points are added in different places. |
Or we could just provide a way for extensions to get the test count that would ensure that they tests were loaded? |
Least intrusive approach is probably declarative. Either a new attribute or a property of Extension attribute could pass info to the engine about what an extension requires. I imagine a flags enum. |
@CharliePoole Yes... although the problem with that is that my extension only activated when a certain environment variable is present, so as not to mess with regular console runs. With an attribute it would activate all the time. Compare to the teamcity plugin which is normally disabled (and enabled somehow, I don't know). Things like that seem to be needed since there is no way (I know of) to enable and disable plugins on the command line, is there? Alternatively: With a new single-purpose interface, it could decide whether or not to activate dynamically: Edit: An even simpler interface, in line with what @rprouse talked about, Edit edit: If there were a general way to enable/disable extensions on the command line (like the hardcoded one for --teamcity), @CharliePoole's declarative approach would be fine also. Don't we want such a feature? Shouldn't be hard to do, I think. |
@cyanite TL;DR I agree with your last suggestion. 😄 Enabling / Disabling extensions is purely an engine feature, which may be accessed by a runner. Conceptually, the extensions themselves are imagined to be always active, if they are called by the engine. Disabling one just means it won't be called. Of course the engine can't possibly know anything about whether an extension should be disabled, since that's a function of the runner (nunit3-console in this case). However, an extension author can decide whether an extension should be initially disabled or enabled, indicating the choice via If we were to allow an extension to disable itself, it would need to have a way to tell the engine that it was disabled. We could do that through a callback interface to the engine or by having the extension implement an interface to be monitored by the engine. That would allow the logic for enablement / disablement to be placed in the extension as you have done. A command-line approach is appropriate IME if the user may or may not want to use the extension on a run-by-run basis. That's exactly the case with the teamcity extension: we only want it to be active when the console is being executed by teamcity. Unfortunately, that forces us to put special logic in the console runner to understand the purpose of the teamcity extension and when to enable it. I didn't much like doing that, but there seemed to be no choice at the time. A more general approach would be to allow the console runner to support extensions as well and to package console and engine extensions together in one assembly. That was the initial design, in fact, but it was never implemented. I would be in favor of two shorter term fixes to the overall problem:
|
@CharliePoole Great. As for disabling/enabling by path, note that As for the optional interface, this would solve my problems nicely. The problem with an interface is that it's annoying to add methods to it later without having to change the code in all extensions. We could also add an interface with a single method returning a configuration object with a base class. Then you'd only need to recompile the extension in most cases, although that part is unavoidable of course, for an engine extension. Option 1 suggestion: interface IExtensionConfiguration
{
bool Enabled { get };
bool NeedsValidTestCaseCount { get };
} Or option 2: interface IExtensionConfiguration
{
ExtensionConfiguration Configuration { get };
}
public class ExtensionConfiguration
{
public virtual bool Enabled => true;
public virtual bool NeedsValidTestCaseCount => false;
} Something like that? |
On second thought, maybe I think we may prefer a flags enum for the specified behaviors the extension needs from the engine. That has two advantages:
So my preferred interface definition would be something like... public interface IExtensionRequirements
{
bool Active { get; }
ExtensionRequirements { get; }
}
[Flags]
public enum ExtensionRequirements
{
None = 0,
NeedsValidStartRunCount = 1
} In this scenario, the engine would have to operate as follows...
This is a big change. The bold lines above are what we currently do, the rest would be new. I think we should define the interface and put it in place and implement the Active / Inactive status in the engine in a single PR. Handling the count should be separate, since it has some complications that depend on what options the user has specified. |
I think we need input from the @nunit/engine-team about the interface before going ahead. |
@cyanite Responding to an earlier comment I missed... the "path" to an extension has nothing to do with the file system. Extension points have a "Path" that describes them uniquely. An extension is implemented by a particular type on a particular extension point. Extension points and extensions are in a many-to-many relationship, so the command-line option would be rather long. IMO, we need a separate issue for command-line enabling / disabling if we want to do it. |
@CharliePoole, for background, in my extension, I activeate/deactivate it based on the presence of an environment variable. I'd prefer a command line option. I don't think we need to be able to disable/enable at the granularity of extension points (paths); I'd be happy with just disable/enable the entire extensions as a whole. Also because extensions conceivably can hook into multiple extensions points that work together. So a command line like As for the interface/enum, it looks good to me. |
If this remains the case in the latest version of the engine, let's remove the count attribute from start-run in v4. It's inaccurate, and there's no clear efficient solution to supply this information at this point in time. It can always be added again as a future feature, if someone comes up with a solution. |
If you eliminate this, then there's no easy way for a GUI to display a progress bar. I think we should figure out when and why this changed. It must have been after I forked the engine for the GUI, because the counts I get work for me. |
Sounds good. I assumed that it had never worked so nobody could be relying on it, but sounds like that's not the case. |
The OP had a very specific situation here, involving an extension. We need to run some tests... probably best to add them to the CI in fact. I'll take a look. |
I finally looked at this. UPDATE: Consequence: still need to do more in-depth debugging to understand this. I believe @mikkelbu 's 2018 comment citing my own 2016 comment is correct. I just need to figure out where that happens in the code. Five years is a long time! In any case, I think there's a tradeoff to be made here. We can easily give a correct value if Regarding |
See this #146 (comment) for further info about why it is the way it is. Possibly, we could give a valid count so long as |
@nunit/engine-team
In case 1, there's no problem. We can easily return a valid number of test cases. In case 2, we can only count the tests if we either ignore the limit or load and count test cases one at a time. Call those option A and option B. With option A we violate the number of processes allowed to be active (the Option B avoids some problems but creates others. Assume we create a single process and count the tests for each assembly using that process - loading and unloading each assembly and keeping a tally. In this case, we will never exceed the Alternatives I can see for V4.0...
All of the above are breaking changes. I'm marking this as blocked until we decide what to do. |
For v4.0, I think we just remove the attribute as the minimal change for correctness. The feature has never worked, so feels more like we're adding a new feature - and that would be a separate discussion that needs someone willing to implement it. |
You suggested that earlier. As I said it's a breaking change for me if it no longer works at all - as opposed to leaving it as it is now. Obviously, that will affect our Compatibility Project. I agree that it has never worked for the console runner. But if we're talking about the engine, it works depending on choices made in the runner. In the case of the console runner, we made the choice to call Run without first calling Load, which results in a zero count. We kept the attribute because other runners might want to use it, calling the engine differently. Can we separate the issue of the console from that of the engine? If you don't like any of the other options I listed, can we resolve this by simply documenting how things work? Regarding "a separate discussion that needs someone willing to implement it"... For some one to pop up and implement a feature, you have to first decide it's a desired feature. IMO that's what discussing the options is about. In addition to the options I listed, there are some others that were previously discussed on this thread. Can we pick a path to follow in the future, even if it's going to be in a post-4.0 release? |
I created issue #921, for documenting |
Sorry, I missed that it was just to do with whether load had been called or not. In that case, does that make this a simple bugfix, that doesn't need to be in 4.0? |
I think so. We made it breaking because of what the OP asked us to do about it. Levels of non-breaking change I can see:
|
This is because the MasterTestRunner is never "loaded" (i.e. the Load method called) before the tests are run, which then makes CountTests return 0. Curiously, no code ever calls Load except some unit tests, which thus seem to test behavior not actually encountered when running the program.
There is also a bug in Load which makes it crash for aggregated test runs.
I don't know if there is a good reason to not always call Load. It seems to work fine for me on a large-ish .nunit test suite with 1 nunit3 test assembly and 50 nunit2.
I will create a pull request which "fixes" the bug in Load (makes it return null where it otherwise crash; no one uses the result anyway), and adds an option to nunit-console, --preload, to load the MasterTestRunner before running the tests. This fixes the bug.
The text was updated successfully, but these errors were encountered: