Add support for cancellationtoken, params, and optional arguments to TestCaseSource#5057
Add support for cancellationtoken, params, and optional arguments to TestCaseSource#5057
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR adds support for cancellation tokens, params arrays, and optional arguments to TestCaseSource attributes, bringing it to feature parity with TestCaseAttribute. The implementation extracts common argument adjustment logic into a new AdjustArgumentsForMethod method in TestCaseParameters, which is used by both TestCaseAttribute and TestCaseSourceAttribute.
- Refactored argument handling logic from TestCaseAttribute into a reusable
AdjustArgumentsForMethodmethod - Added support for params arrays, optional parameters, and implicit CancellationToken injection in TestCaseSource
- Removed .NET 6.0+ preprocessor directives to make generic TestCaseData classes available on all target frameworks
- Added comprehensive test coverage for the new functionality
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs | Added comprehensive tests for params arrays, optional arguments, and cancellation token support in TestCaseSource; removed .NET 6.0+ conditional compilation for generic TestCaseData tests |
| src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs | Added test case using named parameter syntax to verify params array handling |
| src/NUnitFramework/framework/TestCaseData.cs | Added new constructors for generic TestCaseData to support 2 and 3 arguments; removed .NET 6.0+ preprocessor directives to enable usage on all frameworks |
| src/NUnitFramework/framework/Internal/TestCaseParameters.cs | Introduced AdjustArgumentsForMethod method that centralizes logic for handling params arrays, optional parameters, CancellationToken injection, and type conversions |
| src/NUnitFramework/framework/Internal/Extensions/ArgumentsExtensions.cs | Added LastParameterIsParamsArray extension method to detect params array parameters |
| src/NUnitFramework/framework/Attributes/TestCaseSourceAttribute.cs | Updated to use AdjustArgumentsForMethod and modified array unpacking logic to support params arrays; added error handling |
| src/NUnitFramework/framework/Attributes/TestCaseAttribute.cs | Refactored to use the new AdjustArgumentsForMethod method, removing duplicated argument handling logic |
Comments suppressed due to low confidence (1)
src/NUnitFramework/framework/Attributes/TestCaseSourceAttribute.cs:118
- This assignment to methodName is useless, since its value is never read.
var methodName = method.Name;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| catch (Exception ex) | ||
| { | ||
| parms = new TestCaseParameters(ex); | ||
| } |
There was a problem hiding this comment.
Generic catch clause.
There was a problem hiding this comment.
Correct. We want to catch all exceptions here so that we can report them and associate them with the testcase instead of losing a record of it
There was a problem hiding this comment.
This error checking was added by me as the method previously had none. That meant that an error with processing a single testcase would cause the entire collection of them to not be discovered or run.
I primarily added it to make it easier to debug my own other bugs while trying to complete this. Agreed this would be good to add coverage over later.
|
Nice @stevenaw |
|
Phew. CI is happy now. This is ready for review |
There was a problem hiding this comment.
Pull Request Overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
@stevenaw I am actually VERY impressed you have managed to work this out with this code base. A lot of this code is 16 years old (at least), and with way too high cyclomatic complexity, and large methods. To me it is too hard to figure out what the code is actually doing, I am drowning in the details, and then I loose the context.
I mark this just as comments, and not sure if doing anything more here within the scope of the issues you're fixing should be done. I think it is better to mark this code to be refactored and at that time fix the lacking tests too.
Awesome work!
| } | ||
| parms.Arguments = newArgList; | ||
| } | ||
| parms.AdjustArgumentsForMethod(method); |
There was a problem hiding this comment.
Love you're moving all that code off.
There was a problem hiding this comment.
Which I just now understood was moved......
There was a problem hiding this comment.
That's a sign that I found the right abstraction 😀 It took 3-4 tries until I found one that seemed right.
You're right, that entire method is just moved. There are improvements to be had, but I tried to minimize changes to it in order to minimize risk of breaking anything.
| { | ||
| if (parameters.Length == 0) | ||
| { | ||
| return false; |
| /// <summary> | ||
| /// Construct a TestCaseData with a list of arguments. | ||
| /// </summary> | ||
| public TestCaseData(T argument1, T argument2, T argument3) |
There was a problem hiding this comment.
I can quickly add a test over this. This was added for API parity with the pre-existing TestCaseData<T1, T2, T3> since we have a TestCaseData<T> which accepts 1 arg and I was found myself adding support for a TestCaseData<T> which accepts 2 args of the same type.
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
…e.cs Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
713a964 to
33b975d
Compare
manfred-brands
left a comment
There was a problem hiding this comment.
Nice work @stevenaw
I added a few small clean-up commits.
We had an inconsistency between uses of collection initializers for arrays.
Your new code used them, the old code not.
I have aligned them for consistency and readability.
| { | ||
| IParameterInfo lastParameter = parameters[argsNeeded - 1]; | ||
|
|
||
| if (parameters.LastParameterIsParamsArray()) |
There was a problem hiding this comment.
This re-determines lastParameter.
I have split the method and call it here with lastParameter.
| } | ||
|
|
||
| /// <summary> | ||
| /// Construct a TestCaseData with a list of arguments. |
There was a problem hiding this comment.
This is also not a list. I update the comments.
| return n / d; | ||
| } | ||
|
|
||
| [Test, TestCaseSource(nameof(SingleDimensionArray))] |
There was a problem hiding this comment.
We don't need a Test attribute if we have another TestXXX attribute.
|
Thanks @manfred-brands ! |
|
@stevenaw This PR would have been a good candidate for squashing to not pollute the repository with commit messages like: "tmp commit". @OsirisTerje Do we have a rule/guideline for merging vs squash vs rebase? |
|
Good point. I had thought I had kept much of the early experimentation out of the history but I see there was some still there. Agreed this should've been squashed. @OsirisTerje I'd also be curious to hear thoughts on if you'd prefer we always or just prefer a given strategy |
|
@manfred-brands @stevenaw No guidelines for branching/merging (afaik). The @nunit/core-team and the @nunit/framework-team are the only one who can merge a PR, so this only applies to us. Merging has the benefit of being visible afterwards, since Git will know of the task branch. I find that useful. However, if there are many commits (like >3) and they are very much small commits with no new real info in their messages (like your experimentation), I would prefer a squash. I myself also often do a lot of small commits, I find that kind of safer. But, a squash is not directly visible afterwards, so I would say, use it when you should, and not when you should not. There are however no way for a github branch ruleset to have such a rule, so I would then prefer to have no rules - and that "we" do as well as we can, and try to ensure that we don't merge in a multitude of small commits, but squash instead. I see in our docs about Team Practices, we have no branch guidelines. We could add some, but not sure if anybody really would read them. I am not a fan of policing, and strict rules. So common sense, and do as well as one can, is fine with me. PS: One thought; We could add a CoPilot instructions.md expressing such a concern, and then say we should try to always use CoPilot as a reviewer, then we could be warned to merge or squash. PS2: I didn't mention Rebasing. I rarely do that myself, but anyone who like to do that, feel free. |
|
I use rebasing a lot for long-running feature branches but I would not suggest a "rule" about it. If you were documenting guidelines, I'd suggest that as a "Consider" item. I've occasionally merged a series of commits that included a wip or temp item. That's to be avoided but I don't think it's worth worrying about if it doesn't happen often. IMO, talking about this kind of thing on a case by case basis, rather than establishing "rules" is good for a team. |


Relates to #2268
Fixes #1883
Fixes #1281
Fixes #4900
This PR primarily moves existing functionality in
TestCaseAttributeinto a common place where it can also be used byTestCaseSourceAttribute.