Skip to content

Add Constraint ApplyAsync#4813

Merged
stevenaw merged 6 commits intonunit:mainfrom
Dreamescaper:add_constraint_applyasync
Sep 14, 2024
Merged

Add Constraint ApplyAsync#4813
stevenaw merged 6 commits intonunit:mainfrom
Dreamescaper:add_constraint_applyasync

Conversation

@Dreamescaper
Copy link
Copy Markdown
Member

Fixes #4811 .

This PR is created for futher discussions.
Instead of ThatAsync awaiting the result and handling the exception, it delegates it to constraints - via ApplyAsync method.
Therefore, DelayedConstraint supports async tasks as well.

Obviusly, this is a breaking change. It will break anyone who implements IConstraint directly (instead of inheriting from Constraint).
Workarounds are possible to avoid that breaking change, but I'm not sure how ofter this interface is implemented direclty.

Copy link
Copy Markdown
Member

@manfred-brands manfred-brands left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Dreamescaper I like the idea and the implementation is relatively simple and even makes the ThatAsync simpler.
I had one question/suggestion to make this non-breaking.

Comment on lines +67 to +76
/// <summary>
/// Applies the constraint to a delegate that returns the task.
/// The default implementation simply evaluates the delegate and awaits the task
/// but derived classes may override it to provide for delayed processing.
/// </summary>
/// <typeparam name="TActual"></typeparam>
/// <param name="delTask"></param>
/// <returns></returns>
Task<ConstraintResult> ApplyToAsync<TActual>(Func<Task<TActual>> delTask);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add this to an IAsyncConstraint interface?
When resolving the constraint in ThatAsync you can then test on IAsyncConstraint?
If not, throw a NotSupportedException?

That way it would be a non-breaking change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense.
I've made IAsyncConstraint internal for now - in case we'd need to more task-related methods later.

Copy link
Copy Markdown
Member

@manfred-brands manfred-brands left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion to replace BlockingDelay in async DelayedConstraint with Task.Delay

}

[Test]
[Test, Platform(Exclude = "MACOSX", Reason = "Doesn't seem to work correctly with timing, something to ponder later")]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop this as a re-run of the build caused MacOs to pass. The machine github uses for MacOs seems to be very slow.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that other tests have this exclude as well, so I assume that it does happen from time time. Still, removed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and it failed again :)

while ((now = Stopwatch.GetTimestamp()) < delayEnd)
{
if (nextPoll > now)
ThreadUtility.BlockingDelay((int)TimestampDiff(delayEnd < nextPoll ? delayEnd : nextPoll, now).TotalMilliseconds);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we not use non-blocking' delay in an async method?

Suggested change
ThreadUtility.BlockingDelay((int)TimestampDiff(delayEnd < nextPoll ? delayEnd : nextPoll, now).TotalMilliseconds);
await Task.Delay(TimestampDiff(delayEnd < nextPoll ? delayEnd : nextPoll, now));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, done.

}
}
if ((now = Stopwatch.GetTimestamp()) < delayEnd)
ThreadUtility.BlockingDelay((int)TimestampDiff(delayEnd, now).TotalMilliseconds);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here:

Suggested change
ThreadUtility.BlockingDelay((int)TimestampDiff(delayEnd, now).TotalMilliseconds);
await Task.Delay(TimestampDiff(delayEnd, now));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, done.

Copy link
Copy Markdown
Member

@manfred-brands manfred-brands left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Dreamescaper Looks good to me now.

Copy link
Copy Markdown
Member

@stevenaw stevenaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM too. Thanks again for another contribution @Dreamescaper !

@stevenaw stevenaw merged commit fb51025 into nunit:main Sep 14, 2024
@Dreamescaper Dreamescaper deleted the add_constraint_applyasync branch September 16, 2024 07:10
@OsirisTerje
Copy link
Copy Markdown
Member

Just noticed:

Obviusly, this is a breaking change. It will break anyone who implements IConstraint directly (instead of inheriting from Constraint).

Is this so?
Then we need to update the release notes that this is a breaking change.

@Dreamescaper @manfred-brands @stevenaw

@Dreamescaper
Copy link
Copy Markdown
Member Author

@OsirisTerje
No, @manfred-brands suggested a way to avoid it.

@Dreamescaper
Copy link
Copy Markdown
Member Author

@OsirisTerje
On a second thought, there IS a breaking change. Just not the one I've described here.
I've mentioned it here:
#4908

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assert.ThatAsync does not support polling

4 participants