Skip to content

test(e2e): SpeechToText @TestInstallIn swap + FakeSpeechToText#462

Merged
yuga-hashimoto merged 1 commit into
mainfrom
feat/e2e-stt-swap
Apr 19, 2026
Merged

test(e2e): SpeechToText @TestInstallIn swap + FakeSpeechToText#462
yuga-hashimoto merged 1 commit into
mainfrom
feat/e2e-stt-swap

Conversation

@yuga-hashimoto

Copy link
Copy Markdown
Owner

Priority 5: Refactor / Quality

Mirrors #460's TTS swap pattern for the STT boundary so future E2E tests can drive `voicePipeline.startListening()` from a programmatic `SttResult` source instead of needing an emulator microphone.

What changed

Production refactor (minimal, no behavioural change)

  • Extract `provideSpeechToText` from `VoiceModule` into a new `SttModule`
  • Same `DelegatingSttProvider` singleton, same constructor

Tests (`app/src/androidTest/...`)

  • `e2e/fakes/FakeSpeechToText` — Channel-backed `SpeechToText` impl. Tests call `queue(SttResult.Final("..."))` to push results; the next `startListening()` collector receives them in order. `Final` / `Error` close the channel so the Flow completes (matching how the real `AndroidSttProvider` ends a session)
  • `e2e/fakes/FakeSttTestModule` — `@TestInstallIn(replaces = [SttModule::class])` registering the same singleton against both `SpeechToText` and the concrete fake type
  • `e2e/FakeSttPipelineE2ETest` — 4 cases proving the swap works:
    • the interface and the concrete fake resolve to the same Hilt singleton (regression guard against accidentally creating two instances)
    • queued partials + a final emit in order and complete the Flow
    • an `Error` result terminates the session
    • `reset()` allows a second listening session within one test

Scope note

Full wake→STT→tool→TTS pipeline E2E is left to a follow-up because `VoicePipeline.startListening()` touches `VoiceService`, `AudioFocus`, and a beep player that need either a service test rule or further extraction. This PR ships the swap pattern; the chain hookup PR can build on it.

Test plan

## Priority 5: Refactor / Quality

Mirrors #460's TTS swap pattern for the STT boundary so future E2E
tests can drive `voicePipeline.startListening()` from a programmatic
SttResult source instead of needing an emulator microphone.

Production refactor (minimal):
- Extract `provideSpeechToText` from `VoiceModule` into a new
  `SttModule`. No behavioural change — same `DelegatingSttProvider`
  singleton, same constructor.

Test additions:
- `FakeSpeechToText`: Channel-backed `SpeechToText` impl. Tests call
  `queue(SttResult.Final("..."))` to push results; the next
  `startListening()` collector receives them in order. `Final` /
  `Error` close the channel so the Flow completes (matching how the
  real `AndroidSttProvider` ends a session).
- `FakeSttTestModule`: `@TestInstallIn(replaces = [SttModule::class])`
  registering the same singleton against both `SpeechToText` and the
  concrete fake type.
- `FakeSttPipelineE2ETest`: 4 cases proving the swap works:
  * the interface and the concrete fake resolve to the same Hilt
    singleton (regression guard against accidentally creating two
    instances)
  * queued partials + a final emit in order and complete the Flow
  * an `Error` result terminates the session
  * `reset()` allows a second listening session within one test

Scope note: full wake→STT→tool→TTS pipeline E2E is left to a follow-up
because `VoicePipeline.startListening()` touches `VoiceService`,
`AudioFocus`, and a beep player that need either a service test rule
or further extraction. This PR ships the swap pattern; the chain
hookup PR can build on it.

Verification:
- ./gradlew assembleStandardDebugAndroidTest — green
- ./gradlew assembleStandardDebug testStandardDebugUnitTest — green
@yuga-hashimoto yuga-hashimoto merged commit d4d751e into main Apr 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant