Skip to content

Add instrumented smoke tests with Gradle Managed Devices#219

Closed
timusus wants to merge 15 commits intomainfrom
feature/instrumented-smoke-tests
Closed

Add instrumented smoke tests with Gradle Managed Devices#219
timusus wants to merge 15 commits intomainfrom
feature/instrumented-smoke-tests

Conversation

@timusus
Copy link
Copy Markdown
Owner

@timusus timusus commented Mar 28, 2026

Summary

  • 16 instrumented smoke tests covering all critical user paths: launch, library browsing (all 5 tabs), artist/album detail navigation, search with query, playback (tap-to-play, mini player, now playing, queue), home screen, settings, shuffle
  • Gradle Managed Device (Pixel 6, API 34, ATD image) for hermetic test execution — no manual emulator needed
  • waitForView polling utility replaces all Thread.sleep — tests return immediately when conditions are met
  • Split production PlaybackModule into PlaybackModule + PlaybackEngineModule + CastModule for surgical test DI replacement
  • FakePlayback + FakeAudioFocusHelper at the hardware boundary — everything else (QueueManager, repositories, PlaybackManager) runs real
  • Fix: QueueFragment crash on sheet slide after view destroy (auto-cleared value accessed in onSlide callback after onDestroyView)

Test plan

  • ./gradlew :android:app:pixel6Api34AtdDebugAndroidTest — 16/16 pass
  • ./gradlew :android:app:assembleDebug — production build unaffected
  • QueueFragment bug fix verified by re-enabled queue navigation test

timusus added 15 commits March 28, 2026 15:28
Gradle Managed Device (Pixel 6, API 34, ATD image) for hermetic
test execution. Espresso, Compose test, and test orchestrator deps.
Animations disabled for determinism.
Always grants audio focus. No system interaction.
Implements Playback interface with immediate success callbacks.
No real audio — just state tracking for UI verification.
Extract PlaybackEngineModule (ExoPlayer, AudioFocus, PlaybackManager)
and CastModule (Cast service bindings) from PlaybackModule.
Extract DatabaseModule from RepositoryModule.

Change PlaybackManager constructor to accept Playback interface
instead of concrete ExoPlayerPlayback.

No behavioral changes — purely structural split.
TestPlaybackEngineModule: FakePlayback + FakeAudioFocusHelper
TestCastModule: empty (suppresses cast dependencies)
TestDatabaseModule: in-memory Room database
Replaces AppModuleBinds with empty initializer set to avoid
pulling in Cast, Billing, Firebase, etc. during smoke tests.
10 songs across 3 artists and 6 albums. Seeds Room DB and
sets hasOnboarded preference to skip onboarding flow.
4 tests covering critical paths:
- App launch + library visible
- Library tabs show seeded songs
- Tap song activates mini player
- Search screen accessible
4 new tests covering:
- Artist list → artist detail navigation
- Mini player → full now playing expansion
- Queue shows items after playing
- Home screen buttons visible
- artistDrillDown: scope toolbar check to CollapsingToolbarLayout and
  remove redundant recyclerView assertion (multiple coordinatorLayouts)
- tapSong_expandNowPlaying: scope playPauseButton and seekBar to
  sheet1Container, add isClickable to disambiguate PlayStateView from
  its child PlayPauseAnimationView
4 new tests covering:
- Shuffle all from home starts playback
- Search with typed query shows results
- Playlists tab loads without crashing
- Settings bottom sheet opens
Single test that visits all major app destinations:
Home, Library (all 5 tabs), Search, Settings, Artist/Album/Genre
detail screens, Now Playing, and Queue.
Polling-based waitForView utility returns immediately when the view
condition is met instead of always waiting a fixed duration. Better
failure messages on timeout. Removes unnecessary animation sleeps
(animations already disabled in testOptions).
…rView

Navigation tests (3 tests):
- bottomNavAndTabs: visits Home, Library, Search, Settings + all 5 tabs
- detailScreens: artist detail and album detail navigation
- playbackScreens: mini player and now playing expansion

Medium-value tests (4 tests):
- shuffleAll_startsPlayback
- search_typingQuery_showsResults
- playlistsTab_isAccessible
- settings_isAccessible

Replaced all Thread.sleep with polling waitForView utility that
returns immediately when condition is met. Fixed ViewPager2 ambiguous
RecyclerView matches. Fixed album grid shuffle header at position 0.
QueueFragment.onSlide accessed auto-cleared toolbarTitleTextView
after onDestroyView. BottomSheetBehavior's settling animation posts
onSlide callbacks via Choreographer, which can fire after the
fragment's view is destroyed.

Fix: change toolbarTitleTextView, toolbarSubtitleTextView, emptyLabel,
and progressBar from autoCleared to autoClearedNullable with safe
calls. Re-enable queue navigation in smoke test.
@timusus timusus closed this Mar 28, 2026
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