uttrflow / uttrflow/uttrflow-swift
InputDeviceSession's race tests pass without reaching the branches they are named for
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
`Tests/UttrflowAudioTests/InputDeviceSessionTests.swift:176` "closes a device it opened after the close, rather than stranding it open" (for #171) and `:158` "keeps the hole reported when the recording stops mid-reopen" both call `deviceChanged()` and then `close()` straight away. In this run, the close cancels the reopen task during its first `pause`, so `reopen()` returns at `Sources/UttrflowAudio/InputDeviceSession.swift:108`. The branches the tests are about never run:
- line 109: the health check after the pause;
- lines 113 and 119: an open that succeeds after a close landed, followed by `device.close()`;
- line 126: a stop landing while every retry is failing.
Both tests assert conditions (`closes >= opens`, no error reported) that are also true when the race never happens, so they pass either way.
Measured on origin/main `26d7bc1` with `swift test --enable-code-coverage` (5,384 tests, all passing) and `xcrun llvm-cov export` over the test binary. The regions named below have an execution count of 0: no test runs them, so a change that breaks them passes `make verify`.
## Why it matters
Line 119 is the fix for a microphone left open after the recording stopped. That is a privacy bug the user can see in the menu bar's microphone indicator, and a battery cost. As written, the test would still pass if line 119 were deleted.
## How to reproduce
Run the suite with coverage (`swift test --enable-code-coverage --filter "An input device session"`) and show `InputDeviceSession.swift` in `llvm-cov show`. Lines 109-126 have 0 counts. Delete `if !kept { device.close() }` and the suite still passes.
## Acceptance criteria
- The tests control the interleaving instead of relying on timing: a `pause` that suspends until the test releases it, and a device whose `open()` suspends until the test calls `close()` on the session.
- One test drives: device changes, the pause is released, `open()` starts, `session.close()` runs, then `open()` returns. It asserts `closes == opens` and that line 119 ran.
- One test drives a stop during failing retries, and asserts `.gone` with no `.ended` report.
- Deleting line 119, or the `guard` on line 126, makes a test fail.
- No wall-clock sleeps are added (see #393).
- Each new test fails when the branch it covers is broken. Check this by hand before opening the PR: temporarily change the branch (flip the condition or return the other value), see the test fail, then restore it.
Contributor guide
Research direction
Start with Tests/UttrflowAudioTests/InputDeviceSessionTests.swift at lines 158 and 176, then read the reopen path in Sources/UttrflowAudio/InputDeviceSession.swift around lines 108-126. Run the filtered suite with coverage and inspect the named branches. Done means deterministic interleaving tests execute those branches, fail when either guarded behavior is broken, and use no wall-clock sleeps.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 70/100