Skip to content

Robolectric coverage for SpeechRecognitionService#21

Closed
ivan-digital wants to merge 0 commit into
feat/recognition-servicefrom
test/recognition-service-coverage
Closed

Robolectric coverage for SpeechRecognitionService#21
ivan-digital wants to merge 0 commit into
feat/recognition-servicefrom
test/recognition-service-coverage

Conversation

@ivan-digital
Copy link
Copy Markdown
Contributor

@ivan-digital ivan-digital commented May 8, 2026

Stacked on top of #19. Adds JVM unit-test coverage for the new
SpeechRecognitionService and the regressions fixed in 1504fba
(stop-hang, start-race).

Summary

  • Refactor SpeechPipeline from class to interface + internal
    SpeechPipelineImpl. The factory SpeechPipeline(config) is preserved
    via a companion invoke, so the demo app and existing androidTest
    call sites compile unchanged.
  • Open SpeechRecognitionService for test subclassing. Extract three
    protected seams — createPipeline, resolveModelDir, newAudioRecord
    — so JVM tests can run without loading the native library, downloading
    models, or opening a real microphone.
  • Add Robolectric, MockK, androidx.test:core to the :sdk test
    classpath; enable unitTests.isIncludeAndroidResources.

Tests added

SpeechRecognitionServiceTest (5 tests, all run on the JVM in <1 s each):

Test What it pins
startListening_setsUpPipelineAndSignalsReady Happy path: readyForSpeech fires after pipeline init
startListening_concurrentCallReturnsBusy Regression test for the start-race fix — second concurrent start hits ERROR_RECOGNIZER_BUSY
stopListening_flushesPipelineWithSilence Regression test for the stop-hang fix — verifies ~30 zero-frame chunks pushed after onStopListening
startListening_withoutPermission_reportsInsufficient Permission-denied path
transcriptionCompleted_emitsResultsAndTearsDownSession Final event delivers results(...) and closes the pipeline

Tests use a TestableService subclass that overrides the seams, a
FakeSpeechPipeline implementing the new interface, and a MockK-mocked
AudioRecord (returns STATE_INITIALIZED, read returns -1 so the
mic loop exits cleanly).

Test plan

  • ./gradlew :sdk:testDebugUnitTest — 20/20 pass (5 new + 15 existing)
  • ./gradlew :sdk:assembleDebug — green
  • ./gradlew :app:assembleDebug — green (verifies the SpeechPipeline interface refactor doesn't break demo-app callers)
  • ./gradlew :sdk:connectedDebugAndroidTest — 34/34 pass on an arm64 emulator (verifies the existing androidTest suite still runs against the interface)

Notes

  • This PR targets feat/recognition-service so the diff stays focused.
    Retarget to main after Add Android RecognitionService for system-wide voice input #19 merges, or squash both branches into one
    PR if preferred.
  • RecognitionService.Callback has a package-private constructor; MockK
    handles this via its agent. If CI surfaces issues, an alternative is
    to wrap Callback behind a thin testable façade — happy to follow up.

@ivan-digital
Copy link
Copy Markdown
Contributor Author

Closing — all commits absorbed into #19. The cherry-picked Robolectric tests + interface refactor land directly with #19 now.

@ivan-digital ivan-digital force-pushed the test/recognition-service-coverage branch from 40d78b8 to 5a323d6 Compare May 10, 2026 09:06
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