Skip to content

Fix Espresso tests failing on #4692 - #4702

Merged
EmmanuelMess merged 4 commits into
hotfix/3.11.3from
bugfix/espresso-tests
Aug 9, 2026
Merged

Fix Espresso tests failing on #4692#4702
EmmanuelMess merged 4 commits into
hotfix/3.11.3from
bugfix/espresso-tests

Conversation

@TranceLove

Copy link
Copy Markdown
Collaborator

Description

Issue tracker

Addresses #4692

Automatic tests

Updated Espresso tests, no new tests added

Manual tests

  • Done

  • Device:

  • OS:

Build tasks success

Successfully running following tasks on local:

  • ./gradlew assembledebug
  • ./gradlew spotlessCheck

Generative code

  • This PR used generative code tools (GenAI, LLMs, etc.)

  • Model: Claude Sonnet

  • Version: 4.6/5

  • Provider: Github Copilot

Related PR

Related to PR #4692

@TranceLove

TranceLove commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Diagnosis on TabFragmentTest failures (By Copilot)

Root cause: The tests used ActivityTestRule (deprecated) + Espresso.onView(withId(R.id.pager)). The problem: TabFragment is not displayed synchronously on startup — MainActivity.goToMain() runs asynchronously after a Completable completes — so by the time Espresso tried to find R.id.pager the ViewPager2 wasn't in the window hierarchy yet.

Changes made to TabFragmentTest.kt:

  • Replaced ActivityTestRule with ActivityScenario — each test now launches a fresh ActivityScenario via withScenario { } which ensures proper lifecycle management and safe recreate() support.
  • Replaced Espresso swipe actions with direct ViewPager2 API calls — onView(withId(R.id.pager)).perform(swipeLeft()) was replaced with viewPager.setCurrentItem(index, false) called from scenario.onActivity { }.
  • Added Awaitility polling helpers — awaitPager(), awaitTabFragment(), and awaitCurrentItem() poll with a 10-second timeout, tolerating the asynchronous TabFragment loading. All scenario.onActivity {} calls inside the awaitility loops are wrapped in runCatching {} to survive the brief activity destroyed → recreated window during recreate().
  • Replaced rotation+sleep with scenario.recreate() — this is the proper way to simulate configuration changes in modern instrumented tests, and it avoids the async onActivity callback racing against the destroy/recreate lifecycle.
  • Guarded StoragePermissionHelper for API ≥ R — the permission helper launches its own MainActivity which left a stale activity instance on API < 30; it is now only called on Android 11+.

@TranceLove

TranceLove commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Fixes to BackupPrefsFragmentTest (By Copilot)

  • Removed the incorrect runOnUiThread { onView()... } wrapper — Espresso's onView().perform() must run on the instrumentation/test thread, never inside onActivity {} or runOnUiThread {} callbacks (both run on the main/UI thread). Espresso handles UI-thread synchronization internally.
  • Moved all Espresso view actions and JSON-comparison assertions out of activityScenario.onActivity {} onto the test thread, capturing preferences via a lateinit var inside the block for later use.
    Added activityScenario.moveToState(Lifecycle.State.RESUMED) — Espresso requires the activity to be RESUMED to dispatch view actions.
  • Removed the erroneous onView(withId(R.id.home)) click — that ID is a MainActivity-only menu item unrelated to PreferencesActivity. Since exportPrefs() triggers an ACTION_SEND intent to MainActivity, which shows a snackbar with a "Save" action, only onView(withText(R.string.save)).perform(click()) is needed.
  • Added a waitForFile() polling helper since the actual file write to storagePath happens asynchronously after the click.

Copilot also reported a caveat that the test may still fail on API 21 devices, where legacy JAR verifier may be incompatible with modern APK signing on very old Android runtimes.

@TranceLove
TranceLove marked this pull request as ready for review August 5, 2026 16:03
@TranceLove
TranceLove requested review from EmmanuelMess and VishnuSanal and a lite review from Copilot August 5, 2026 16:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Android instrumentation (Espresso) tests to be more robust/reliable across API levels and lifecycle changes, addressing the Espresso failures reported in #4692 by moving away from deprecated ActivityTestRule, rotation hacks, and fixed sleeps.

Changes:

  • Refactors TabFragmentTest to use ActivityScenario + scenario.recreate() and Awaitility-based polling instead of orientation changes and swipe actions.
  • Stabilizes BackupPrefsFragmentTest by using the runtime external storage path, ensuring the activity is RESUMED for Espresso interactions, and polling for async file creation before asserting contents.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
app/src/androidTest/java/com/amaze/filemanager/ui/fragments/TabFragmentTest.kt Reworks tab/state-saving tests to use ActivityScenario recreation and Awaitility waits instead of rotation/swipes.
app/src/androidTest/java/com/amaze/filemanager/ui/fragments/BackupPrefsFragmentTest.kt Fixes export test flakiness by using dynamic storage path, correct lifecycle state for Espresso, and waiting for async file writes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/src/androidTest/java/com/amaze/filemanager/ui/fragments/TabFragmentTest.kt Outdated
Comment thread app/src/androidTest/java/com/amaze/filemanager/ui/fragments/TabFragmentTest.kt Outdated
@TranceLove
TranceLove force-pushed the bugfix/espresso-tests branch from 305019e to feebe13 Compare August 6, 2026 06:13
- TabFragmentTest use back actions to swipe instead of programmatically

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (3)

app/src/androidTest/java/com/amaze/filemanager/ui/fragments/TabFragmentTest.kt:167

  • awaitPager() calls scenario.onActivity {} inside an Awaitility polling loop without guarding against IllegalStateException during activity recreation (e.g., after rotateScreen). If onActivity throws while no Activity is in a valid state, Awaitility will fail the test immediately instead of retrying, making this flaky.
        await().atMost(10, TimeUnit.SECONDS).until {
            scenario.onActivity { activity ->
                pager = activity.findViewById(R.id.pager)
            }

app/src/androidTest/java/com/amaze/filemanager/ui/fragments/TabFragmentTest.kt:268

  • awaitOrientation() evaluates currentOrientation(scenario) inside Awaitility without catching exceptions. During rotation, ActivityScenario.onActivity inside currentOrientation() can throw transiently while the Activity is being recreated, which can cause a hard test failure instead of letting Awaitility retry.
        await().atMost(10, TimeUnit.SECONDS).until {
            currentOrientation(scenario) == expectedOrientation
        }

app/src/androidTest/java/com/amaze/filemanager/test/StoragePermissionHelper.kt:48

  • ActivityScenario.launch(MainActivity::class.java) is never closed. Because grantManageStoragePermission() is called from multiple @Before methods, this can leak Activities across tests and cause flakiness/interference. Prefer wrapping the launch + permission UI flow in ActivityScenario.launch(...).use { ... } (or try/finally with scenario.close()) so the Activity is always torn down.
            ActivityScenario.launch(MainActivity::class.java)

@EmmanuelMess
EmmanuelMess enabled auto-merge August 9, 2026 17:28
@EmmanuelMess
EmmanuelMess merged commit 2f03bac into hotfix/3.11.3 Aug 9, 2026
4 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.

3 participants