Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/copilot/Async/invokeAsync_generate_test_instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# InvokeAsync Unit Test Instructions for Copilot

## Method Signatures to Test

```csharp
// Async callback returning ValueTask
public async Task InvokeAsync(Func<CancellationToken, ValueTask> callback, CancellationToken cancellationToken = default)

// Async callback returning ValueTask<T>
public async Task<T> InvokeAsync<T>(Func<CancellationToken, ValueTask<T>> callback, CancellationToken cancellationToken = default)

// Sync callback returning T
public async Task<T> InvokeAsync<T>(Func<T> callback, CancellationToken cancellationToken = default)

// Sync callback returning void
public async Task InvokeAsync(Action callback, CancellationToken cancellationToken = default)
```

## Required Test Coverage

Please create comprehensive unit tests for each overload that verify:

### Core Functionality
- **UI Thread Delegation**: Verify the callback executes on the UI thread (different from calling thread)
- **Cancellation Support**: Test cancellation works even when callback doesn't support it (sync overloads)
- **Async Cancellation**: Test cancellation works when callback supports it (async overloads with CancellationToken)
- **Exception Propagation**: Verify exceptions from callbacks are properly propagated to caller

### Edge Cases
- **Handle Not Created**: Verify `InvalidOperationException` when control handle isn't created
- **Pre-cancelled Token**: Verify early return when token is already cancelled
- **Multiple Concurrent Calls**: Test thread safety with overlapping invocations
- **Reentry Scenarios**: Test calling InvokeAsync from within a callback

### Cancellation Scenarios
- **External Cancellation**: Cancel token while callback is queued/running
- **Callback Cancellation**: For async overloads, test cancellation within the callback itself
- **Registration Cleanup**: Verify cancellation registrations are properly disposed

### Return Value Testing
- **Generic Overloads**: Test proper return value handling for `Task<T>` variants
- **Void Overload**: Test completion signaling for `Action` overload

### Performance/Resource Testing
- **Memory Leaks**: Verify no leaked registrations or task completion sources
- **Async Context**: Verify ConfigureAwait behavior and sync context handling

## Test Structure Guidance

- Use a test control with proper handle creation for UI thread tests
- Use `Thread.CurrentThread.ManagedThreadId` to verify thread marshalling
- Use `CancellationTokenSource` with timeouts for cancellation tests
- Include both immediate and delayed cancellation scenarios
- Test with both short-running and long-running callbacks
- Use appropriate async test patterns with proper awaiting

Create tests that are robust, deterministic, and cover both happy path and error conditions.
6 changes: 6 additions & 0 deletions Winforms.sln
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GDI", "GDI", "{D619FF8C-D99
.github\copilot\GDI\GDIPlus-copilot-instructions.md = .github\copilot\GDI\GDIPlus-copilot-instructions.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Async", "Async", "{7BFA3A16-A19A-4FCE-AE1E-D1187BD92D4C}"
ProjectSection(SolutionItems) = preProject
.github\copilot\Async\invokeAsync_generate_test_instructions.md = .github\copilot\Async\invokeAsync_generate_test_instructions.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1180,6 +1185,7 @@ Global
{D4D97D78-D213-45DF-B003-9C4C9F2E5E1C} = {8B4B1E09-B3C7-4044-B223-94EDEC1CAA20}
{442C867C-51C0-8CE5-F067-DF065008E3DA} = {77FEDB47-F7F6-490D-AF7C-ABB4A9E0B9D7}
{D619FF8C-D99A-48AB-B16B-2F0E819B46D5} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{7BFA3A16-A19A-4FCE-AE1E-D1187BD92D4C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7B1B0433-F612-4E5A-BE7E-FCF5B9F6E136}
Expand Down
1 change: 1 addition & 0 deletions src/System.Windows.Forms/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

// Publicly shipped API

[assembly: SuppressMessage("Naming", "CA1725:Parameter names should match base declaration", Justification = "Public API", Scope = "member", Target = "~M:System.Windows.Forms.ButtonBase.OnKeyDown(System.Windows.Forms.KeyEventArgs)")]
[assembly: SuppressMessage("Naming", "CA1725:Parameter names should match base declaration", Justification = "Public API", Scope = "member", Target = "~M:System.Windows.Forms.ButtonBase.OnKeyUp(System.Windows.Forms.KeyEventArgs)")]
[assembly: SuppressMessage("Naming", "CA1725:Parameter names should match base declaration", Justification = "Public API", Scope = "member", Target = "~M:System.Windows.Forms.ButtonBase.OnMouseDown(System.Windows.Forms.MouseEventArgs)")]
Expand Down
Loading
Loading