Skip to content

Conversation

@La002
Copy link
Contributor

@La002 La002 commented Jan 17, 2026

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

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
@La002 La002 changed the title Moved tests using time to use synctest in new files Testing: use synctest for timing-dependent tests Jan 17, 2026
@La002
Copy link
Contributor Author

La002 commented Jan 25, 2026

@findleyr @jba following up, is this still relevant ? Are any changes required?

@La002
Copy link
Contributor Author

La002 commented Jan 28, 2026

@maciej-kisiel Pushed with the changes discussed here

Copy link
Contributor

@maciej-kisiel maciej-kisiel left a 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.


// wait for the server to exit
// synctest will detect deadlock if server doesn't exit
synctest.Wait()
Copy link
Contributor

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.

Copy link
Contributor Author

@La002 La002 Feb 3, 2026

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

})

cs, ss, cleanup := basicClientServerConnection(t, c, nil, nil)
_ = cs // Dummy usage to avoid "declared and not used" error.
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, done fb973b1

}

// 3. Client should receive the notification
synctest.Wait()
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done bb74dc2

waitForNotification := func(t *testing.T, name string) {
t.Helper()
time.Sleep(notificationDelay * 2)
synctest.Wait()
Copy link
Contributor

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().

Copy link
Contributor Author

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" {
Copy link
Contributor

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?

Copy link
Contributor Author

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) {
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added the todo fb973b1

errs <- err
}()
time.Sleep(2 * time.Millisecond)
if i < batchSize-1 {
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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()?

Copy link
Contributor Author

@La002 La002 Feb 3, 2026

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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

cancel()

// Wait for all goroutines to be blocked (cancellation delivered).
synctest.Wait()
Copy link
Contributor

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().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done bb74dc2

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.

Testing: use synctest throughout

3 participants