Issue4824 replace delegates#5196
Conversation
c4b9f76 to
d3deb37
Compare
|
There is one compatibility issue: Func<object?> code = () => null;
Assert.That(code, Is.Not.Null);Previously this was executed as To test a Assert.That((object?)code, Is.Not.Null);or obtain the value indirectly. Assert.That(() => code, Is.Not.Null);Note this is the same behaviour if the user had used an ActualValueDelegate<object?> code = () => null;
Assert.That(code, Is.Not.Null);Note that |
|
@manfred-brands So a major change here is that with your changes, all delegates are executed, not only the ones that have the Throws constraints? Current behavior (version 4.5.X):
|
Regarding your Func<T>? getValue = DetermineGetValueMethod();
Assert.That(getValue, Is.Not.Null);
T value = getValue.Invoke(); |
|
Ok, but with your changes in this PR, are the delegates executed ? Ref
|
Yes |
|
@manfred-brands Is this PR just waiting for nunit/nunit.analyzers#982 ? |
Also on a decision if we want to put this in the 4.6 branch instead possible with a setting to switch behaviour as it then would be a non-breaking feature. It would get my vote. |
|
I'm just catching up after being away for a week, but if I read right then it sounds like we are invoking the lambda in all cases now? I don't think I have an "informed" opinion yet, but my first instinct is that the previous behavior felt a bit more natural to me, as did the workaround. Previously, where we only executed for throws, one could "work around" this by just manually invoking the method and passing the return value in. It also followed the "opt-in" model where the lambda could be "opted into" executing by having calling code do that. If that's right, then having to cast a To help me understand, was there a use case towards moving to the new model? |
|
@stevenaw to clarify, the new behaviour for The only difference is for a variable declared as |
bc8a64d to
aa698ac
Compare
We cannot make such a setting as we cannot distinguish between a variable of type |
Ok that's great then, thanks for confirming @manfred-brands . I have no concerns with how you've described this |
I'll check my PR for the Func with parameters.
We cannot do a switch as that would disable all code using lambda.
|
|
Sounds good to me. |
|
Just noted this analyzer issue nunit/nunit.analyzers#982, so that comment above is ok |
OsirisTerje
left a comment
There was a problem hiding this comment.
Looks good.
Just some places where CS1573 probably should be silenced, left one comment on that.
This is meant to replace TestDelegate and AsyncTestDelegate respectively. To prevent overload resolution conflicts with Lambda, the old methods are deprioritized
Revert when nunit/nunit.analyzers#982 is released
aa698ac to
0752378
Compare
|
@OsirisTerje I retargeted to Locally, I get build errors which look related to what you are working on: |
|
Re local build fails: Tested locally here, and it WOMM. But this test is flaky, once the Phase2 is merged, it should be better, I hope. Wonder a bit why we never get rid of that appveyor build...... |
| /// <param name="code">A func returning the value to be tested</param> | ||
| /// <returns>A ConstraintResult</returns> | ||
| [Obsolete("This was never implemented and will be removed.")] | ||
| ConstraintResult ApplyTo<TActual>(ref TActual actual); |
There was a problem hiding this comment.
question:
@manfred-brands I'm only just starting to dig into this PR, but this change caught my eye. Changing an interface method's signature would typically be a breaking change, but the PR has been repointed to main now instead of release/5.x. Do we need to adjust this to ensure no one is broken?
EDIT: Similar questions and concerns about related changes to the Constraint base class too
There was a problem hiding this comment.
Thanks @stevenaw, you are correct:
This was marked Obsolete in 4.4 and as I initially targeted 5.X I thought to remove it.
I could rewrite the PR and remove the commit removing this overload and change that to a separate PR targeting 5.X if you prefer.
How breaking is it?
I people implemented it previously, nothing from our side is calling it.
The implementation we had, simply called the normal ApplyTo.
A Github Override Search shows lots of clones/copies of NUnit Framework.
A GitHub Call Search only shows one usage in HarmonyTest which calls in to the base class.
Yes, the change to IConstraint to require new overloads is more breaking. Again I was targeting 5.X previously.
I can take the same approach as #4813 where a new IAsyncConstraint interface was added deriving from IConstraint with runtime checks to see if the resolved constraint is actually async:
if (constraint is not IAsyncConstraint asyncConstraint)
{
throw new NotSupportedException($"Constraint {constraint?.GetType().Name} does not support async execution.");
}I'm not in favour of converting compile time to runtime checks.
But we could do that for 4.6 and change it to the current for 5.X/
Or we can retarget this PR back to 5.X?
My suspicion is that GH must've been having issues at the time the PR was last updated. @manfred-brands I was able to resolve the issue previously in a PR by amending the most recent commit to include whitespace, which forced GH to reevaluate the branch due to the changing commit hash. The most recent GH Actions build seemed to have passed. I have rerun it as well just now to see if that also helps EDIT: I wonder if it's the change in target branch that is complicating this. That build I linked seems to still reference v5 and should be ignored |
I don't think it is too harmful (will probably come back to bite me, but....). Then we should continue the discussion on versioning we started, and also consider if we should be more aggressive with using the major versioning. Right now we have a self-inflicting wound/rule to keep as few major versions as possible. We don't have to do that. But this is for #5223 . |

Fixes #4824
Note this requires nunit/nunit.analyzers#982
When completed remove TODO sections.
I have added separate commits to mark the Delegate methods as Obsolete if we want to do that as well.