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

Commit b74760d8 authored by Johannes Gallmann's avatar Johannes Gallmann Committed by Android (Google) Code Review
Browse files

Merge "[STL] Expose STL element state for testing" into main

parents f8ab19e4 7bc5e9dc
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -154,11 +154,11 @@ internal fun Modifier.element(
 * An element associated to [ElementNode]. Note that this element does not support updates as its
 * arguments should always be the same.
 */
private data class ElementModifier(
    private val layoutImpl: SceneTransitionLayoutImpl,
internal data class ElementModifier(
    internal val layoutImpl: SceneTransitionLayoutImpl,
    private val currentTransitionStates: List<TransitionState>,
    private val content: Content,
    private val key: ElementKey,
    internal val content: Content,
    internal val key: ElementKey,
) : ModifierNodeElement<ElementNode>() {
    override fun create(): ElementNode =
        ElementNode(layoutImpl, currentTransitionStates, content, key)
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.compose.animation.scene.testing

import androidx.compose.ui.semantics.SemanticsNode
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.Element.Companion.AlphaUnspecified
import com.android.compose.animation.scene.ElementModifier
import com.android.compose.animation.scene.Scale

val SemanticsNode.lastAlphaForTesting: Float?
    get() = elementState.lastAlpha.takeIf { it != AlphaUnspecified }

val SemanticsNode.lastScaleForTesting: Scale?
    get() = elementState.lastScale.takeIf { it != Scale.Unspecified }

private val SemanticsNode.elementState: Element.State
    get() {
        val elementModifier =
            layoutInfo
                .getModifierInfo()
                .map { it.modifier }
                .filterIsInstance<ElementModifier>()
                .firstOrNull()
        requireNotNull(elementModifier) {
            "No ElementModifier found. Did you use the Modifier.element(...)?"
        }
        return with(elementModifier) {
            val element =
                requireNotNull(layoutImpl.elements[key]) {
                    "No element found in STL layout with key: ${key.testTag}"
                }
            requireNotNull(element.stateByContent[content.key]) {
                "No state found for given content key: ${content.key.testTag}"
            }
        }
    }
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.compose.animation.scene.testing

import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.Scale
import com.android.compose.animation.scene.TestElements
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.testTransition
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ElementStateAccessTest {

    @get:Rule val rule = createComposeRule()

    @Test
    fun testElementStateAccess() {
        rule.testTransition(
            fromSceneContent = { Box(Modifier.element(TestElements.Foo).size(50.dp)) },
            toSceneContent = { Box(Modifier) },
            transition = {
                spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
                fade(TestElements.Foo)
                scaleDraw(TestElements.Foo, scaleX = 0f, scaleY = 0f)
            },
            fromScene = SceneA,
            toScene = SceneB,
        ) {
            before {
                val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(1f)
                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(1f, 1f))
            }

            at(32) {
                val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.5f)
                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.5f, 0.5f))
            }

            at(64) {
                val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0f)
                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0f, 0f))
            }
            after { onElement(TestElements.Foo).assertDoesNotExist() }
        }
    }
}