Skip to content

Inject the clock and add a test target so lifecycle sequencing can be asserted without real time #4

Description

@nashysolutions-bot

What breaks. UIStream.run calls Task.sleep(for:) directly, and the package ships no test target (1.2.0: five source files under Sources/, nothing under Tests/). So nothing that consumes it can assert its sequencing without waiting real time, and the package's own lifecycle guarantees (animateIn, rest or block, animateOut, endDelay, hidden; .reject on cancel before animateIn; animateOut then hidden on cancel mid-show) are enforced by nothing. The concrete consequence today: sprintly#4 asks whether Sprintly's splash converges onto this package, and the recommendation is to converge, but the port cannot be measured. The instrument for that (run the same members under a real clock and an instant one, diff the emission logs) needs a seam this package does not have.

Fix. Two parts, one PR:

  1. Inject the clock. public init(clock: any Clock<Duration> = ContinuousClock()), stored, and every Task.sleep(for:) in run and animateOutAndHide becomes clock.sleep(for:). Default argument is a value, not a closure literal; see the note below on why that matters.
  2. Add Tests/UIStreamTests with a test clock that returns immediately and records requested durations, and assert at least: full lifecycle order for a timed member; a blocker waits for unblock(); cancelAll() mid-show emits animateOut then hidden and nothing after; enqueueExclusively emits .reject for members still queued and starts the replacement only after the interrupted member is hidden. Each test seen to fail on a planted defect before it is trusted.

Do not put an async closure literal in a public default argument. On Swift 6.2.4 that shape is compiled into every importing module with a different async frame size and the linker mixes the copies (swiftlang/swift#92017, hit on Sprintly's ui-queue and fixed there in sprintly#15). A Clock value as the default is fine; a { try await Task.sleep(...) } default is not.

Origin. Surfaced by the UIQueue demo work behind sprintly#4 on 2026-09-08; the comparison against ui-queue is on that ticket.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions