Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c2e53a2f authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Add Truth subjects for TransitionState

This CL adds Truth subjects for TransitionState and
TransitionState.Transition so that asserting on them provides better
error messages.

Bug: 290930950
Test: atest PlatformComposeSceneTransitionLayoutTests
Flag: N/A
Change-Id: I0af1131b27428c4699d02a89baebd311a86c5a39
parent 2975d22e
Loading
Loading
Loading
Loading
+9 −34
Original line number Diff line number Diff line
@@ -32,12 +32,11 @@ import com.android.compose.animation.scene.NestedScrollBehavior.EdgeWithPreview
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.TransitionState.Idle
import com.android.compose.animation.scene.TransitionState.Transition
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
@@ -145,10 +144,8 @@ class DraggableHandlerTest {
        }

        fun assertIdle(currentScene: SceneKey) {
            assertThat(transitionState).isInstanceOf(Idle::class.java)
            assertWithMessage("currentScene does not match")
                .that(transitionState.currentScene)
                .isEqualTo(currentScene)
            assertThat(transitionState).isIdle()
            assertThat(transitionState).hasCurrentScene(currentScene)
        }

        fun assertTransition(
@@ -158,34 +155,12 @@ class DraggableHandlerTest {
            progress: Float? = null,
            isUserInputOngoing: Boolean? = null
        ) {
            assertThat(transitionState).isInstanceOf(Transition::class.java)
            val transition = transitionState as Transition

            if (currentScene != null)
                assertWithMessage("currentScene does not match")
                    .that(transition.currentScene)
                    .isEqualTo(currentScene)

            if (fromScene != null)
                assertWithMessage("fromScene does not match")
                    .that(transition.fromScene)
                    .isEqualTo(fromScene)

            if (toScene != null)
                assertWithMessage("toScene does not match")
                    .that(transition.toScene)
                    .isEqualTo(toScene)

            if (progress != null)
                assertWithMessage("progress does not match")
                    .that(transition.progress)
                    .isWithin(0f) // returns true when comparing 0.0f with -0.0f
                    .of(progress)

            if (isUserInputOngoing != null)
                assertWithMessage("isUserInputOngoing does not match")
                    .that(transition.isUserInputOngoing)
                    .isEqualTo(isUserInputOngoing)
            val transition = assertThat(transitionState).isTransition()
            currentScene?.let { assertThat(transition).hasCurrentScene(it) }
            fromScene?.let { assertThat(transition).hasFromScene(it) }
            toScene?.let { assertThat(transition).hasToScene(it) }
            progress?.let { assertThat(transition).hasProgress(it) }
            isUserInputOngoing?.let { assertThat(transition).hasIsUserInputOngoing(it) }
        }

        fun onDragStarted(
+31 −40
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollable
@@ -43,7 +42,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.approachLayout
@@ -64,6 +62,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.subjects.assertThat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -78,7 +77,6 @@ class ElementTest {
    @get:Rule val rule = createComposeRule()

    @Composable
    @OptIn(ExperimentalComposeUiApi::class)
    private fun SceneScope.Element(
        key: ElementKey,
        size: Dp,
@@ -496,7 +494,6 @@ class ElementTest {
    }

    @Test
    @OptIn(ExperimentalFoundationApi::class)
    fun elementModifierNodeIsRecycledInLazyLayouts() = runTest {
        val nPages = 2
        val pagerState = PagerState(currentPage = 0) { nPages }
@@ -654,8 +651,7 @@ class ElementTest {
            }
        }

        assertThat(state.currentTransition).isNull()
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(state.transitionState).isIdle()

        // Swipe by half of verticalSwipeDistance.
        rule.onRoot().performTouchInput {
@@ -691,9 +687,9 @@ class ElementTest {

        val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
        fooElement.assertTopPositionInRootIsEqualTo(0.dp)
        val transition = state.currentTransition
        val transition = assertThat(state.transitionState).isTransition()
        assertThat(transition).isNotNull()
        assertThat(transition!!.progress).isEqualTo(0.5f)
        assertThat(transition).hasProgress(0.5f)
        assertThat(animatedFloat).isEqualTo(50f)

        rule.onRoot().performTouchInput {
@@ -702,8 +698,8 @@ class ElementTest {
        }

        // Scroll 150% (Scene B overscroll by 50%)
        assertThat(transition.progress).isEqualTo(1.5f)
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(transition).hasProgress(1.5f)
        assertThat(transition).hasOverscrollSpec()
        fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)
        // animatedFloat cannot overflow (canOverflow = false)
        assertThat(animatedFloat).isEqualTo(100f)
@@ -714,8 +710,8 @@ class ElementTest {
        }

        // Scroll 250% (Scene B overscroll by 150%)
        assertThat(transition.progress).isEqualTo(2.5f)
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(transition).hasProgress(2.5f)
        assertThat(transition).hasOverscrollSpec()
        fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
        assertThat(animatedFloat).isEqualTo(100f)
    }
@@ -766,8 +762,7 @@ class ElementTest {
            }
        }

        assertThat(state.currentTransition).isNull()
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(state.transitionState).isIdle()
        val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
        fooElement.assertTopPositionInRootIsEqualTo(0.dp)

@@ -779,10 +774,9 @@ class ElementTest {
            moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
        }

        val transition = state.currentTransition
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(transition).isNotNull()
        assertThat(transition!!.progress).isEqualTo(-0.5f)
        val transition = assertThat(state.transitionState).isTransition()
        assertThat(transition).hasOverscrollSpec()
        assertThat(transition).hasProgress(-0.5f)
        fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)

        rule.onRoot().performTouchInput {
@@ -791,8 +785,8 @@ class ElementTest {
        }

        // Scroll 150% (Scene B overscroll by 50%)
        assertThat(transition.progress).isEqualTo(-1.5f)
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(transition).hasProgress(-1.5f)
        assertThat(transition).hasOverscrollSpec()
        fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
    }

@@ -825,13 +819,12 @@ class ElementTest {
            moveBy(Offset(0f, layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
        }

        val transition = state.currentTransition
        assertThat(transition).isNotNull()
        val transition = assertThat(state.transitionState).isTransition()
        assertThat(animatedFloat).isEqualTo(100f)

        // Scroll 150% (100% scroll + 50% overscroll)
        assertThat(transition!!.progress).isEqualTo(1.5f)
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(transition).hasProgress(1.5f)
        assertThat(transition).hasOverscrollSpec()
        fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 0.5f)
        assertThat(animatedFloat).isEqualTo(100f)

@@ -841,8 +834,8 @@ class ElementTest {
        }

        // Scroll 250% (100% scroll + 150% overscroll)
        assertThat(transition.progress).isEqualTo(2.5f)
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(transition).hasProgress(2.5f)
        assertThat(transition).hasOverscrollSpec()
        fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 1.5f)
        assertThat(animatedFloat).isEqualTo(100f)
    }
@@ -882,13 +875,11 @@ class ElementTest {
            moveBy(Offset(0f, layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
        }

        val transition = state.currentTransition
        assertThat(transition).isNotNull()
        transition as TransitionState.HasOverscrollProperties
        val transition = assertThat(state.transitionState).isTransition()

        // Scroll 150% (100% scroll + 50% overscroll)
        assertThat(transition.progress).isEqualTo(1.5f)
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(transition).hasProgress(1.5f)
        assertThat(transition).hasOverscrollSpec()
        fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * (transition.progress - 1f))
        assertThat(animatedFloat).isEqualTo(100f)

@@ -900,8 +891,8 @@ class ElementTest {
        rule.waitUntil(timeoutMillis = 10_000) { transition.progress < 1f }

        assertThat(transition.progress).isLessThan(1f)
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(transition.bouncingScene).isEqualTo(transition.toScene)
        assertThat(transition).hasOverscrollSpec()
        assertThat(transition).hasBouncingScene(transition.toScene)
        assertThat(animatedFloat).isEqualTo(100f)
    }

@@ -980,13 +971,13 @@ class ElementTest {

        val transitions = state.currentTransitions
        assertThat(transitions).hasSize(2)
        assertThat(transitions[0].fromScene).isEqualTo(SceneA)
        assertThat(transitions[0].toScene).isEqualTo(SceneB)
        assertThat(transitions[0].progress).isEqualTo(0f)
        assertThat(transitions[0]).hasFromScene(SceneA)
        assertThat(transitions[0]).hasToScene(SceneB)
        assertThat(transitions[0]).hasProgress(0f)

        assertThat(transitions[1].fromScene).isEqualTo(SceneB)
        assertThat(transitions[1].toScene).isEqualTo(SceneC)
        assertThat(transitions[1].progress).isEqualTo(0f)
        assertThat(transitions[1]).hasFromScene(SceneB)
        assertThat(transitions[1]).hasToScene(SceneC)
        assertThat(transitions[1]).hasProgress(0f)

        // First frame: both are at x = 0dp. For the whole transition, Foo is at y = 0dp and Bar is
        // at y = layoutSize - elementSoze = 100dp.
@@ -1153,7 +1144,7 @@ class ElementTest {
        state.finishTransition(aToB, SceneB)
        state.finishTransition(bToC, SceneC)
        rule.waitForIdle()
        assertThat(state.currentTransition).isNull()
        assertThat(state.transitionState).isIdle()

        // The interruption values should be unspecified and deltas should be set to zero.
        val foo = layoutImpl.elements.getValue(TestElements.Foo)
+9 −8
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
@@ -165,10 +166,10 @@ class InterruptionHandlerTest {
        // pair, and its velocity is used when animating the progress back to 0.
        val bToA = checkNotNull(state.setTargetScene(SceneA, coroutineScope = this))
        testScheduler.runCurrent()
        assertThat(bToA.fromScene).isEqualTo(SceneA)
        assertThat(bToA.toScene).isEqualTo(SceneB)
        assertThat(bToA.currentScene).isEqualTo(SceneA)
        assertThat(bToA.progressVelocity).isEqualTo(progressVelocity)
        assertThat(bToA).hasFromScene(SceneA)
        assertThat(bToA).hasToScene(SceneB)
        assertThat(bToA).hasCurrentScene(SceneA)
        assertThat(bToA).hasProgressVelocity(progressVelocity)
    }

    @Test
@@ -191,10 +192,10 @@ class InterruptionHandlerTest {
        // and its velocity is used when animating the progress to 1.
        val bToA = checkNotNull(state.setTargetScene(SceneB, coroutineScope = this))
        testScheduler.runCurrent()
        assertThat(bToA.fromScene).isEqualTo(SceneA)
        assertThat(bToA.toScene).isEqualTo(SceneB)
        assertThat(bToA.currentScene).isEqualTo(SceneB)
        assertThat(bToA.progressVelocity).isEqualTo(progressVelocity)
        assertThat(bToA).hasFromScene(SceneA)
        assertThat(bToA).hasToScene(SceneB)
        assertThat(bToA).hasCurrentScene(SceneB)
        assertThat(bToA).hasProgressVelocity(progressVelocity)
    }

    companion object {
+3 −2
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.assertSizeIsEqualTo
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -157,8 +158,8 @@ class MovableElementTest {
                            fromSceneZIndex: Float,
                            toSceneZIndex: Float
                        ): SceneKey {
                            assertThat(transition.fromScene).isEqualTo(TestScenes.SceneA)
                            assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
                            assertThat(transition).hasFromScene(TestScenes.SceneA)
                            assertThat(transition).hasToScene(TestScenes.SceneB)
                            assertThat(fromSceneZIndex).isEqualTo(0)
                            assertThat(toSceneZIndex).isEqualTo(1)

+25 −19
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.TestScenes.SceneD
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.animation.scene.transition.link.StateLink
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
@@ -322,8 +323,8 @@ class SceneTransitionLayoutStateTest {
        // Go back to A.
        state.setTargetScene(SceneA, coroutineScope = this)
        testScheduler.advanceUntilIdle()
        assertThat(state.currentTransition).isNull()
        assertThat(state.transitionState.currentScene).isEqualTo(SceneA)
        assertThat(state.transitionState).isIdle()
        assertThat(state.transitionState).hasCurrentScene(SceneA)

        // Specific transition from A to B.
        assertThat(
@@ -477,23 +478,24 @@ class SceneTransitionLayoutStateTest {
                        overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
                    }
            )
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        val transition = assertThat(state.transitionState).isTransition()
        assertThat(transition).hasNoOverscrollSpec()

        // overscroll for SceneA is NOT defined
        progress.value = -0.1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()

        // scroll from SceneA to SceneB
        progress.value = 0.5f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()

        progress.value = 1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()

        // overscroll for SceneB is defined
        progress.value = 1.1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(state.currentTransition?.currentOverscrollSpec?.scene).isEqualTo(SceneB)
        val overscrollSpec = assertThat(transition).hasOverscrollSpec()
        assertThat(overscrollSpec.scene).isEqualTo(SceneB)
    }

    @Test
@@ -507,23 +509,25 @@ class SceneTransitionLayoutStateTest {
                        overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
                    }
            )
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()

        val transition = assertThat(state.transitionState).isTransition()
        assertThat(transition).hasNoOverscrollSpec()

        // overscroll for SceneA is defined
        progress.value = -0.1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
        assertThat(state.currentTransition?.currentOverscrollSpec?.scene).isEqualTo(SceneA)
        val overscrollSpec = assertThat(transition).hasOverscrollSpec()
        assertThat(overscrollSpec.scene).isEqualTo(SceneA)

        // scroll from SceneA to SceneB
        progress.value = 0.5f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()

        progress.value = 1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()

        // overscroll for SceneB is NOT defined
        progress.value = 1.1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()
    }

    @Test
@@ -534,22 +538,24 @@ class SceneTransitionLayoutStateTest {
                progress = { progress.value },
                sceneTransitions = transitions {}
            )
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()

        val transition = assertThat(state.transitionState).isTransition()
        assertThat(transition).hasNoOverscrollSpec()

        // overscroll for SceneA is NOT defined
        progress.value = -0.1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()

        // scroll from SceneA to SceneB
        progress.value = 0.5f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()

        progress.value = 1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()

        // overscroll for SceneB is NOT defined
        progress.value = 1.1f
        assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
        assertThat(transition).hasNoOverscrollSpec()
    }

    @Test
Loading