Skip to content

uts: define normative fake-time semantics (ADVANCE_TIME guarantee, hard-gate vs advisory, authoring rule) - #518

Open
sacOO7 wants to merge 1 commit into
mainfrom
uts/fake-time-semantics-and-authoring-rule
Open

uts: define normative fake-time semantics (ADVANCE_TIME guarantee, hard-gate vs advisory, authoring rule)#518
sacOO7 wants to merge 1 commit into
mainfrom
uts/fake-time-semantics-and-authoring-rule

Conversation

@sacOO7

@sacOO7 sacOO7 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Problem statement

ADVANCE_TIME(ms) is the single knob UTS timer tests use to drive time-dependent behavior, but three SDKs implemented it with silently different semantics:

  • ably-cocoa and ably-js hard-gate virtual time — nothing timer-driven runs until ADVANCE_TIME moves virtual time past the due point.
  • ably-java is advisory — its ConnectionManager blocks in a real, timed monitor wait that the mock clock cannot fully intercept, so ADVANCE_TIME accelerates the wait but the same work will also fire once real time elapses.

This ambiguity caused a real CI flake in ably-java (ably/ably-java#1231): a spec-shaped "no retry before advance" assertion — asserting the absence of timer-driven behavior before an advance — was simply unassertable on the advisory model, because the advisory model cannot guarantee that scheduled work has not yet run.

What this changes

Three normative additions to mock_websocket.md §Timer Mocking (new ### Fake-time semantics (normative)), plus a self-consistency fix:

  1. Guarantee. "After ADVANCE_TIME(ms) returns, virtual time has advanced by ms, and every unit of scheduled work whose due time is at or before the new virtual time has been run to quiescence (including work scheduled by that work, provided it too is due within the advanced interval)." This is the only timing behavior tests may rely on.
  2. Hard gate vs. advisory latitude. Hard gate is RECOMMENDED; advisory is PERMITTED where the SDK is forced into a real timed wait by its threading model. An implementation MUST document which model it provides. An advisory implementation still honors the Guarantee — it merely offers a weaker exclusion (it does not guarantee the absence of firing before advance).
  3. Authoring rule (normative for spec tests). Tests MUST NOT assert the absence of timer-driven behavior before the corresponding ADVANCE_TIME; such negative "nothing has happened yet" assertions are valid only under a hard gate and are therefore non-portable. Includes a Disallowed (negative, pre-advance) example and an Allowed (cross the boundary, then own the resulting event) example.
  4. respond_with_success(connected_message?: ProtocolMessage) self-consistency fix. The signature is reconciled to an optional argument, matching the doc's own no-arg example at L267.

This is a STRENGTHENING, not a behavior change. The Guarantee is satisfied by all three SDKs today — ably-java's FakeClock gained a run-to-quiescence loop in the same ably-java PR — and no existing green test is invalidated.

What this deliberately does NOT do

It does not remediate the existing spec tests that violate the new authoring rule: heartbeat_test.md (L245/247/336/349/778/866/879/886) and realtime_timeouts.md (L257-259). Those pass on hard-gate SDKs today and are tracked as a follow-up hygiene task.

Cross-SDK context

Companion mock-infra changes are landing alongside this:

Review guide

Read uts/realtime/unit/helpers/mock_websocket.md §"Fake-time semantics (normative)" (the three subsections: Guarantee, Hard gate vs. advisory, Authoring rule) plus the respond_with_success signature at L76. uts/rest/unit/helpers/mock_http.md gains only a one-line cross-reference. The diff is additive and normative-clarifying — no existing normative statement is weakened or removed.

Three SDKs implemented ADVANCE_TIME with silently different semantics:
ably-cocoa and ably-js hard-gate virtual time, while ably-java is advisory
because its ConnectionManager blocks in a real timed wait that the mock clock
cannot fully intercept. This ambiguity caused a real CI flake in ably-java
(ably/ably-java#1231): a "no retry before advance" assertion was unassertable
on the advisory model.

This strengthens mock_websocket.md §Timer Mocking with:

- Guarantee: after ADVANCE_TIME(ms) returns, virtual time has advanced by ms
  and every unit of due scheduled work has run to quiescence, including
  cascades scheduled by that work that are themselves due within the interval.
- Hard gate vs. advisory latitude: hard gate is RECOMMENDED; advisory is
  PERMITTED where the SDK blocks in a real timed wait. Implementations MUST
  document which model they provide; advisory still honors the Guarantee.
- Authoring rule (normative for spec tests): tests MUST NOT assert the absence
  of timer-driven behavior before the corresponding ADVANCE_TIME, since that
  is only valid under a hard gate and is therefore non-portable. Includes
  Disallowed (negative, pre-advance) and Allowed (cross-boundary, own the
  event) examples.

Also reconciles respond_with_success(connected_message?: ProtocolMessage) to
an optional arg, matching the doc's own no-arg example at L267.

mock_http.md gains a one-line cross-reference so HTTP timeout tests inherit
the same normative fake-time contract.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant