Repository navigation
fix(engine): await the Task of async runtime dynamic tests - #7363
Conversation
DynamicTest<T>.TestMethod is an Expression<Action<T>>, so compiling it discarded the Task or ValueTask returned by an async test method and any failure after its first await. Compile the lambda body into a Func<object, object?> that returns the value so the invoker awaits it.
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Compile and await runtime test results src/TUnit.Engine/Services/TestRegistry.cs |
The runtime invoker compiles an object-returning delegate and awaits returned Task and ValueTask values. It also validates that the lambda has one parameter. |
Verify asynchronous dynamic test results tests/TUnit.TestProject/Bugs/_7249/AsyncRuntimeDynamicTests.cs, tests/TUnit.Engine.Tests/Bug7249AsyncRuntimeDynamicTests.cs, tests/TUnit.TestProject/Bugs/_7204/SharedInstanceAfterDisposalTests.cs |
The tests register passing and failing synchronous and asynchronous dynamic methods. The engine test checks that six results include three failures with the expected asynchronous test names. A comment about keeping a test method synchronous was removed; the method itself remains unchanged. |
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix · Severity of issue fixed: Medium
Possibly related PRs
- thomhurst/TUnit#7248: Its test changes document the discarded-task behavior addressed by this PR.
- thomhurst/TUnit#7296: It handles unobserved task faults in session results; this PR instead awaits the runtime dynamic-test task and reports its failure as a test result.
Merge Risk: 
Minimal · up to dc0de
-
No actionable issue is established that would prevent merging after normal checks.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed-
Full details: Linked Issues check
-
Explanation
Issue
#7249requires runtime tests added with AddDynamicTest to report failures raised after await. TestRegistry.CreateRuntimeTestInvoker now compiles the lambda body to return its value, awaits Task and ValueTask results, and converts ValueTask<T> to Task<T> before awaiting. The new regression tests cover failing Task, ValueTask, and ValueTask<T> methods, plus passing async and synchronous methods. The engine test checks the expected six-test result and failure names.
Full details: Out of Scope Changes check
-
Explanation
The changes support issue
#7249. The Native AOT dependency supports the ValueTask<T> invocation path. The engine and test-project changes add regression coverage. Removing the#7204comment removes a workaround note made obsolete by this fix. No unrelated changes are present in the reviewed diff.
-
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 3 files.
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches
-
Generate docstrings
-
- Commit to this branch
- Create a new PR
Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
A rabbit tests a task at night,
It waits until the work is right.
A ValueTask gets watched as well,
And failed tests ring a clearer bell.
The sync tests hop along their way,
While async errors will not stray.
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
ReviewThe change looks correct. I found nothing that needs fixing.
Optional nits:
LGTM. |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
[Medium impact] The PR appears safe to merge; no blocking issue remains. SummaryRuntime dynamic tests now wait for returned async work instead of dropping it.
Reviews (3) · Last reviewed commit: "fix(engine): keep ValueTask<T>.AsTask me..." · Reviewed by Greptile |
Addresses review: a boxed ValueTask<T> matched neither Task nor ValueTask in the runtime dynamic test invoker, so failures after an await still passed. Convert it to its Task<T> via AsTask in the compiled expression, and add a regression case.
|
@codex review |
ReviewThe fix is sound. Re-typing the lambda body to return a boxed value, and awaiting it when it is a What I checked:
Minor, non-blocking:
LGTM. |
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
ReviewThe fix looks correct and well-scoped. I found no blocking issues.
Non-blocking suggestions:
Overall this is a good fix. The CI |
…c tests (#7249) Native AOT trimmed the reflection metadata of ValueTask<T>.AsTask, so the GetMethod lookup returned null and Expression.Call threw ArgumentNullException when a dynamic test returned ValueTask<T>.
ReviewThe change looks correct and well-scoped.
Minor, non-blocking suggestions:
Nothing blocking. LGTM. |
Summary
A dynamic test added at run time with
TestContext.AddDynamicTestwhose method returnsTaskorValueTaskpassed even when the method threw after its firstawait.DynamicTest<T>.TestMethodis anExpression<Action<T>>, soTestRegistry.CreateRuntimeTestInvokercompiled anAction<T>and the returned task was dropped. The invoker now compiles the lambda body into aFunc<object, object?>that returns the body's value (boxed, ornullforvoidbodies), and awaits it when it is aTaskorValueTask. AValueTask<T>body is converted to itsTask<T>withAsTaskfirst, since a boxedValueTask<T>matches neither. The rewrite substitutes the parameter with anExpressionVisitorinstead of building an untyped lambda, so it adds no newRequiresDynamicCodeusage. Native AOT keeps no reflection metadata forValueTask<T>.AsTaskby default, so a[DynamicDependency]onValueTask<>.AsTaskkeeps the lookup working there (without it, CI's AOT jobs failed withArgumentNullExceptionfromExpression.Call). Synchronous exceptions are no longer wrapped inTargetInvocationException, because the delegate is no longer invoked through reflection.Also removes the
#7204regression test comment that worked around this bug.Tests
tests/TUnit.TestProject/Bugs/_7249adds five dynamic tests (asyncTask,ValueTaskandValueTask<int>each failing after await, async passing, sync passing). It has noEngineTestproperty because it contains intentional failures.tests/TUnit.Engine.Tests/Bug7249AsyncRuntimeDynamicTestsasserts 6 total, 3 passed, 3 failed with the three expected failing names.Local runs (net10.0, Release):
/*/TUnit.TestProject.Bugs._7249/*/*: 6 total, 3 failed (the expected ones), in both source-generated and--reflectionmodes. Without the fix: 5 total, 0 failed (reproduces the bug)./*/*DynamicTests/*/*: 29/29 passed in both modes./*/TUnit.TestProject.Bugs._7204/*/*: 6/6 passed in both modes.PublishTestProjectAOTModule):/*/TUnit.TestProject.Bugs._7249/*/*gives 6 total, 3 failed, the three expected names. Without the[DynamicDependency], a NativeAOT probe reproduced CI'sArgumentNullException (Parameter 'method').TUnit.Engine.Testsbuilds; not run locally perAGENTS.md, left to CI.Closes #7249
Summary by CodeRabbit
Task,ValueTask, orValueTask<T>are reported correctly.