-
Notifications
You must be signed in to change notification settings - Fork 343
Testing: use synctest for timing-dependent tests #756
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Converted tests with time.After/time.Sleep to use Go 1.25's synctest package in new *_go125_test.go files for instant, deterministic execution. Tests using real I/O remain timing-based. Fixes modelcontextprotocol#680
|
@maciej-kisiel Pushed with the changes discussed here |
maciej-kisiel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the PR and and patience with the review. I added some comments.
mcp/cmd_go125_test.go
Outdated
|
|
||
| // wait for the server to exit | ||
| // synctest will detect deadlock if server doesn't exit | ||
| synctest.Wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is synctest.Wait() required here? If I understand synctest's documentation correctly, I believe blocking on onServerExit should be sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I did misunderstand the behaviour of synctest.Wait(). Thanks for all the review comments
Removed all the places it wasn't needed bb74dc2
mcp/elicitation_go125_test.go
Outdated
| }) | ||
|
|
||
| cs, ss, cleanup := basicClientServerConnection(t, c, nil, nil) | ||
| _ = cs // Dummy usage to avoid "declared and not used" error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it is pre-existing, but maybe we can leave the campsite better:
Wouldn't putting _ in place of cs one line above work as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, done fb973b1
mcp/elicitation_go125_test.go
Outdated
| } | ||
|
|
||
| // 3. Client should receive the notification | ||
| synctest.Wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly here, we're blocking on a channel, so I don't think synctest.Wait() call is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done bb74dc2
mcp/mcp_go125_test.go
Outdated
| waitForNotification := func(t *testing.T, name string) { | ||
| t.Helper() | ||
| time.Sleep(notificationDelay * 2) | ||
| synctest.Wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly here, I believe it should work without synctest.Wait().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done bb74dc2
|
|
||
| // ===== resources ===== | ||
| t.Log("Testing resources") | ||
| if runtime.GOOS != "windows" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we leave the TODO as a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done fb973b1
| "github.com/google/jsonschema-go/jsonschema" | ||
| ) | ||
|
|
||
| func TestEndToEnd_Synctest(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you leave the following TODO for me above the function signature?
// TODO: split this test into multiple test cases that target specific functionality
It was already big before your modifications and will be even harder to debug now that we don't have t.Run calls that would separate the results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the todo fb973b1
mcp/mcp_go125_test.go
Outdated
| errs <- err | ||
| }() | ||
| time.Sleep(2 * time.Millisecond) | ||
| if i < batchSize-1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused, if batchSize is 1, would this if ever execute? It seems the only possible value for i is 0, so this will be 0 < 1-1 = 0 which is false. If you can confirm this is never executed, let's just remove this test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes its not executed. The comment above also mentions the buggy situation
Removed as part of 246ae8b
| defer cs.Close() | ||
|
|
||
| // Let the connection establish properly first | ||
| time.Sleep(30 * time.Millisecond) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be replaced with synctest.Wait()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added 246ae8b
| // Wait for keepalive to detect the failure and close the client | ||
| // Check periodically with simulated time advancement | ||
| for i := 0; i < 40; i++ { // 40 iterations * 25ms = 1 second total | ||
| time.Sleep(25 * time.Millisecond) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have the background why this loop was introduced, but wouldn't a single sleep longer than the keepalive time be sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might have been useful for earlier test, to exit early and not have to wait the whole duration of whatever longer sleep time would have been specified. But now with synctest, time is not an issue, so a sufficient sleep time of 1s should be good enough
mcp/mcp_go125_test.go
Outdated
| cancel() | ||
|
|
||
| // Wait for all goroutines to be blocked (cancellation delivered). | ||
| synctest.Wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it should work without synctest.Wait().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done bb74dc2
Testing: use synctest for timing-dependent tests
Converted timing-based tests to use Go 1.25's synctest for instant, deterministic execution with simulated time.
Moved converted tests to *_go125_test.go files. Tests using real I/O (exec.Command, httptest) remain timing-based as they require actual system operations.
Fixes #680