uttrflow / uttrflow/uttrflow-swift

Test polling helpers give up after 30 s of wall-clock time and fail CI when the runner stalls, while five other loops have no limit and hang instead

Open
#629 0 comments 0 reactions 0 assignees View on GitHub
area:general bug good first issue P2
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

## What happens

Tests that wait for work running beside them each carry a private copy of a polling loop, and the copies disagree about how to stop.

**A 30-second wall-clock ceiling**, then a recorded failure:

- `Tests/UttrflowPipelineTests/DictationPipelineTurnTests.swift:144` (`eventually`, failure at line 150)
- `Tests/UttrflowPipelineTests/DictationControllerTests.swift:678`
- `Tests/UttrflowAudioTests/AVAudioCaptureEngineTests.swift:191` (`settle`)
- `Tests/UttrflowAudioTests/InputDeviceSessionTests.swift:193` (`untilSettled`)
- `Tests/UttrflowTests/DiagnosticsEngineProbeTests.swift:13` (`settled`)

On a loaded runner the whole test process can stall for about 30 seconds. When it does, the ceiling expires although nothing is wrong. `DictationPipelineTurnTests.swift:150` failed four to seven tests at once in CI runs 34815318669 and 34796754962 on `main` and 34792317387 on a branch, all on 2026-09-14; in the same runs dozens of unrelated one-millisecond tests reported "passed after ~30 seconds".

**No limit at all**, in suites that have no `.timeLimit` either, so a regression hangs the run rather than failing it (the #395 class):

- `Tests/UttrflowPipelineTests/DictationPipelineSettingsTests.swift:69`, `:221`, `:224`
- `Tests/UttrflowPredictTests/IdleReleaseInterleavingTests.swift:95` (`until`)
- `Tests/UttrflowLocalModelTests/ReloadableWeightsTests.swift:94`

`Tests/UttrflowSpeechTests/BackedSpeechEngineTurnTests.swift:76` already does the right thing: it yields without a clock and relies on the suite's `.timeLimit(.minutes(1))` to end a wait that never finishes.

## Why it matters

A contributor whose pull request touches nothing near dictation can get a red CI run from these, and the ones without a limit leave a local `swift test` sitting silently until killed. Seven copies of the same loop also mean each new test writes an eighth.

## Acceptance criteria

- One helper in `Sources/UttrflowTestSupport/` (for example `Eventually.swift`, `public func eventually(_ condition: () async -> Bool) async`), which polls by yielding or a short sleep and has **no** wall-clock ceiling of its own.
- Every file listed above uses it instead of its private loop.
- Every suite that uses it has `.timeLimit(.minutes(1))` (or longer where a test genuinely needs it), so a condition that never holds fails the test instead of hanging the run.
- No test's pass or fail depends on how many seconds have elapsed.
- `make verify` passes.

## Where to start

- `Sources/UttrflowTestSupport/ManualClock.swift` shows the style of the shared test helpers.
- `Tests/UttrflowSpeechTests/BackedSpeechEngineTurnTests.swift:71-83` is the pattern to copy.
- To check a changed wait still fails when it should, temporarily break the condition (e.g. `eventually { false }`) and confirm the time limit reports it.
- Read [CONTRIBUTING.md](https://github.com/uttrflow/uttrflow-swift/blob/main/CONTRIBUTING.md) first, and say on this issue that you are taking it.
- Before pushing, run `make verify` (with `DEVELOPER_DIR` pointing at Xcode 26.6 or later). It is the same command CI runs.

**Size:** S to M, about 2–3 hours.

Contributor guide

Open the contributing guide

Research direction

Read CONTRIBUTING.md, Sources/UttrflowTestSupport/ManualClock.swift, and Tests/UttrflowSpeechTests/BackedSpeechEngineTurnTests.swift:71-83 first. Replace the private polling loops in the listed test files with one shared helper, add the required suite time limits, and run make verify; temporarily using eventually { false } should confirm that a stalled condition fails through the suite limit.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
testing
Issue type
Refactor
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.