TWiStErRob / net.twisterrob.astro

0 stars 0 forks source link

Flaky RootViewWithoutFocusException on CI and local #116

Open TWiStErRob opened 1 month ago

TWiStErRob commented 1 month ago
back button hides the picker (net.twisterrob.astro.screen.bazi.DatePickerDialogUiTest) failed
modules/screen/bazi/build/outputs/androidTest-results/connected/debug/TEST-emulator-5554 - 14-_screen_bazi-.xml [took 14s]
androidx.test.espresso.base.RootViewPicker$RootViewWithoutFocusException: Waited for the root of the view hierarchy to have window focus and not request layout for 10 seconds. If you specified a non default root matcher, it may be picking a root that never takes focus. Root:
Root{application-window-token=android.view.ViewRootImpl$W@2a7eb81, window-token=android.view.ViewRootImpl$W@2a7eb81, has-window-focus=false, layout-params-type=1, layout-params-string={(0,0)(fillxfill) ty=BASE_APPLICATION wanim=0x10302fe
fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
pfl=NO_MOVE_ANIMATION FORCE_DRAW_STATUS_BAR_BACKGROUND FIT_INSETS_CONTROLLED
bhv=DEFAULT
fitSides=}, decor-view-string=DecorView{id=-1, visibility=VISIBLE, width=1080, height=1920, has-focus=false, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params={(0,0)(fillxfill) ty=BASE_APPLICATION wanim=0x10302fe
fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
pfl=NO_MOVE_ANIMATION FORCE_DRAW_STATUS_BAR_BACKGROUND FIT_INSETS_CONTROLLED
bhv=DEFAULT
fitSides=}, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=3}}
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:1841)
at androidx.test.espresso.base.EspressoExceptionHandler.handleSafely(EspressoExceptionHandler.java:34)
at androidx.test.espresso.base.EspressoExceptionHandler.handleSafely(EspressoExceptionHandler.java:26)
at androidx.test.espresso.base.DefaultFailureHandler$TypedFailureHandler.handle(DefaultFailureHandler.java:158)
at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:120)
at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:385)
at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:212)
at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:140)
at androidx.test.espresso.Espresso.pressBack(Espresso.java:237)
at net.twisterrob.astro.test.compose.PressBackKt.pressBack(pressBack.kt:17)
at net.twisterrob.astro.screen.bazi.DatePickerDialogUiTest.back button hides the picker(DatePickerDialogUiTest.kt:32)

Instrumentation Merged Results 34.zip Instrumentation Test Results 34.zip logs_29015296931.zip

https://issuetracker.google.com/issues/235520295

Quite consistent repro: modules/screen/bazi/src/androidTest/kotlin/net/twisterrob/astro/screen/bazi/TimePickerDialogUiTest.kt

package net.twisterrob.astro.screen.bazi

import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import net.twisterrob.astro.test.compose.pressBack
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.robolectric.annotation.Config
import java.time.LocalTime
import java.time.ZonedDateTime

@RunWith(AndroidJUnit4::class)
@Config(qualifiers = "long") // TimePickerDialog seems to be too tall for default Robolectric screen size.
class TimePickerDialogUiTest {

    @get:Rule
    val compose = createComposeRule()

    @Test
    fun x1() {
        val mockListeners = TestListeners()
        compose.setContent(listeners = mockListeners)

        compose.pressBack()

        verify(mockListeners.onHideTimePicker).invoke()
        mockListeners.verifyNoMoreInteractions()
    }

    @Test
    fun x2() {
        val mockListeners = TestListeners()
        compose.setContent(listeners = mockListeners)

        compose.pressBack()

        verify(mockListeners.onHideTimePicker).invoke()
        mockListeners.verifyNoMoreInteractions()
    }

    private fun ComposeContentTestRule.setContent(
        listeners: TestListeners = TestListeners(),
        state: ZonedDateTime = ZonedDateTime.now(),
    ) {
        setContent {
            TimePickerDialog(
                state = state,
                onSelectTime = listeners.onSelectTime,
                onHideTimePicker = listeners.onHideTimePicker,
                onResetToNow = listeners.onResetToNow,
            )
        }
    }

    private class TestListeners(
        val onSelectTime: (LocalTime) -> Unit = mock(),
        val onHideTimePicker: () -> Unit = mock(),
        val onResetToNow: () -> Unit = mock(),
    ) {
        fun verifyNoMoreInteractions() {
            verifyNoMoreInteractions(
                onSelectTime,
                onHideTimePicker,
                onResetToNow,
            )
        }
    }
}
TWiStErRob commented 1 month ago

First instance: image

TWiStErRob commented 1 month ago

Reported https://issuetracker.google.com/issues/371512565 as specific issue