AL+

Language: RO (română) EN (English)

Email us: paul@alplustech.com

Looking back at the week of 19–25 February 2025

Timeout tests need a clock and a synchronization point

Go's experimental synctest separated elapsed time from concurrent progress. Use that distinction for timeout tests while retaining evidence from real I/O.

Citiți articolul în română

A cancellation test should establish that the callback has had an opportunity to run before deciding whether cancellation worked. Sleeping for an arbitrary interval leaves that opportunity to the machine's load. The useful idea behind Go's experimental synctest was to control both time and observable concurrent progress. For timeout policy, that is a better starting point than repeatedly increasing a delay until a test usually passes.

Looking back from November 2026, the historical boundary matters. Go 1.24 was released on 11 February 2025. The 19 February explanation of testing/synctest described an experiment enabled with GOEXPERIMENT=synctest, outside the compatibility promise. Its API used Run and Wait. This article discusses that February experiment, not a later stable API contract.

Elapsed time does not establish progress

Suppose a proposed test cancels an operation, sleeps briefly and checks a flag. If the flag is unchanged, two explanations remain possible. Cancellation might be broken, or the callback might not yet have run. Increasing the sleep makes the second explanation less likely on some machines, while making every successful run wait longer. It does not state the ordering the assertion needs.

A test for the opposite condition has the same problem. Waiting and observing that nothing happened cannot generally prove that nothing was ready to happen. The test needs a defined point at which relevant work has either completed or cannot proceed until the next deliberate action. Naming that point turns an uncertain observation into an assertion about a particular state of the system.

The February design placed the function passed to Run and its child goroutines inside a bubble. It combined a fake clock with Wait as a synchronization operation. Virtual time could advance when the bubble was durably blocked. Blocking that could be resolved by an outside event did not meet that condition. These are properties of the documented experiment, not a claim that the operating system scheduler became deterministic.

For an application team, the distinction changes what a failure means. A failed policy test should point toward the policy or the test's model. If machine load remains a plausible explanation for every failure, engineers have another uncertainty to investigate before they can act on the result. Removing that uncertainty is valuable even without claiming a measured improvement in test duration.

Put assertions around a policy transition

Consider an illustrative timeout policy that keeps an operation active until its deadline. A useful test would establish the state before expiry, move the controlled clock to the boundary, allow the associated work to settle and inspect the state afterwards. The assertion before the boundary matters: a test that checks only eventual cancellation could accept an implementation that cancels immediately.

Explicit cancellation deserves a separate scenario. Its question is whether requesting cancellation triggers the agreed cleanup, regardless of whether the deadline has arrived. A proposed test should also define what cleanup means. Observing a cancelled context alone would be insufficient if the application promises to release a resource or stop sending results.

Keep those scenarios small enough that the synchronization assumptions remain visible. A giant test that mixes deadline policy, remote requests and unrelated workers can lose the very boundary that virtual time was intended to clarify. A test that cannot say which operation enables the next step is difficult to trust, even if it finishes quickly.

This is a recommendation for test design, not an experiment performed for this article. The exact implementation should follow the chosen Go version. Copying a current example into a discussion of February 2025 would blur both the API chronology and the experimental compatibility limit. Teams maintaining old test suites need that distinction before deciding which examples apply.

A real connection remains useful evidence

The strongest counterargument is that a timeout often exists because a real dependency is unreliable. A database call, socket or remote server has behaviour a fake clock cannot reproduce by itself. A policy test can establish how your code reacts to a controlled event while leaving open whether the real integration actually produces that event in the expected form.

That objection supports retaining focused integration tests. For an illustrative network client, one test might cover local cancellation policy and another might verify what happens when the real transport is interrupted. The second test earns its cost by examining the boundary that the first deliberately controls. Neither result should be presented as proof of every possible scheduling interleaving or every failure of the external service.

The bubble's limits reinforce this division. External I/O should not be assumed to advance under virtual time or to provide the same synchronization evidence as internal operations. A hung real connection can remain a hung real connection. Likewise, a fake clock is not a substitute for checking resource use under representative load when that is the production concern.

Make the evidence readable to the next maintainer

A useful review begins with one flaky test and its intended promise. Write down the transition being tested, the observation that proves completion and the part of the environment that remains real. Only then choose the mechanism. An explicit channel handshake may already express the needed ordering; virtual time is especially useful when the policy itself depends on deadlines.

For custom software development, ask for a test whose failure identifies an actionable broken promise. Keep timeout and cancellation semantics controllable, and retain real I/O checks where integration behaviour matters. The February synctest experiment supplied a concrete way to think about that separation. Its lasting design value is the insistence that “enough time passed” and “the relevant work reached a known point” are different pieces of evidence.