[go: nahoru, domu]

Merge "Update LazyListFocusMoveTest to use ParameterizedComposeTestRule" into androidx-main
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/list/LazyListFocusMoveTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/list/LazyListFocusMoveTest.kt
index 98952e5..403f3d5 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/list/LazyListFocusMoveTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/list/LazyListFocusMoveTest.kt
@@ -27,8 +27,11 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.ParameterizedComposeTestRule
+import androidx.compose.testutils.createParameterizedComposeTestRule
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection
@@ -46,8 +49,6 @@
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.test.junit4.ComposeContentTestRule
-import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.LayoutDirection.Ltr
@@ -65,83 +66,101 @@
 @OptIn(ExperimentalComposeUiApi::class)
 @MediumTest
 @RunWith(Parameterized::class)
-class LazyListFocusMoveTest(param: Param) {
+class LazyListFocusMoveTest(param: FocusDirectionWrapper) {
+
     @get:Rule
-    val rule = createComposeRule()
+    val rule = createParameterizedComposeTestRule<Param>()
 
     // We need to wrap the inline class parameter in another class because Java can't instantiate
     // the inline class.
+    class FocusDirectionWrapper(val direction: FocusDirection)
+
     class Param(
-        val focusDirection: FocusDirection,
         val reverseLayout: Boolean,
         val layoutDirection: LayoutDirection
     ) {
-        override fun toString() = "focusDirection=$focusDirection " +
+        override fun toString() =
             "reverseLayout=$reverseLayout " +
-            "layoutDirection=$layoutDirection"
+                "layoutDirection=$layoutDirection"
     }
 
-    private val focusDirection = param.focusDirection
-    private val reverseLayout = param.reverseLayout
-    private val layoutDirection = param.layoutDirection
-    private val initiallyFocused: FocusRequester = FocusRequester()
+    private val focusDirection = param.direction
+    private var initiallyFocused: FocusRequester = FocusRequester()
     private var isLazyListFocused by mutableStateOf(false)
     private val isFocused = mutableMapOf<Int, Boolean>()
     private lateinit var lazyListState: LazyListState
     private lateinit var focusManager: FocusManager
 
     companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun initParameters() = buildList {
-            for (direction in listOf(Previous, Next, Left, Right, Up, Down, Enter, Exit)) {
-                for (reverseLayout in listOf(true, false)) {
-                    for (layoutDirection in listOf(Ltr, Rtl)) {
-                        add(Param(direction, reverseLayout, layoutDirection))
-                    }
+        val ParamsToRun = buildList {
+            for (reverseLayout in listOf(true, false)) {
+                for (layoutDirection in listOf(Ltr, Rtl)) {
+                    add(Param(reverseLayout, layoutDirection))
                 }
             }
         }
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun initParameters() = buildList {
+            for (direction in arrayOf(Previous, Next, Left, Right, Up, Down, Enter, Exit)) {
+                add(FocusDirectionWrapper(direction))
+            }
+        }
+    }
+
+    private fun resetTestCase() {
+        isLazyListFocused = false
+        isFocused.clear()
+        initiallyFocused = FocusRequester()
     }
 
     @Test
     fun moveFocusAmongVisibleItems() {
         // Arrange.
         rule.setTestContent {
-            lazyList(50.dp, lazyListState) {
+            lazyList(50.dp, it, lazyListState) {
                 item { FocusableBox(0) }
                 item { FocusableBox(1, initiallyFocused) }
                 item { FocusableBox(2) }
             }
         }
-        rule.runOnIdle { initiallyFocused.requestFocus() }
+        with(rule) {
+            forEachParameter(ParamsToRun) { param ->
+                runOnIdle { initiallyFocused.requestFocus() }
 
-        // Act.
-        val success = rule.runOnIdle {
-            focusManager.moveFocus(focusDirection)
-        }
-
-        // Assert.
-        rule.runOnIdle {
-            assertThat(success).apply { if (focusDirection == Enter) isFalse() else isTrue() }
-            when (focusDirection) {
-                Left -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 2 else 0]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 0 else 2]).isTrue()
+                // Act.
+                val success = runOnIdle {
+                    focusManager.moveFocus(focusDirection)
                 }
 
-                Right -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 0 else 2]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 2 else 0]).isTrue()
-                }
+                // Assert.
+                runOnIdle {
+                    assertThat(success).apply {
+                        if (focusDirection == Enter) isFalse() else isTrue()
+                    }
+                    when (focusDirection) {
+                        Left -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 2 else 0]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 0 else 2]).isTrue()
+                        }
 
-                Up -> assertThat(isFocused[if (reverseLayout) 2 else 0]).isTrue()
-                Down -> assertThat(isFocused[if (reverseLayout) 0 else 2]).isTrue()
-                Previous -> assertThat(isFocused[0]).isTrue()
-                Next -> assertThat(isFocused[2]).isTrue()
-                Enter -> assertThat(isFocused[1]).isTrue()
-                Exit -> assertThat(isLazyListFocused).isTrue()
-                else -> unsupportedDirection()
+                        Right -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 0 else 2]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 2 else 0]).isTrue()
+                        }
+
+                        Up -> assertThat(isFocused[if (param.reverseLayout) 2 else 0]).isTrue()
+                        Down -> assertThat(isFocused[if (param.reverseLayout) 0 else 2]).isTrue()
+                        Previous -> assertThat(isFocused[0]).isTrue()
+                        Next -> assertThat(isFocused[2]).isTrue()
+                        Enter -> assertThat(isFocused[1]).isTrue()
+                        Exit -> assertThat(isLazyListFocused).isTrue()
+                        else -> unsupportedDirection()
+                    }
+                }
+                runOnIdle { runBlocking { lazyListState.scrollToItem(0) } }
+                resetTestCase()
             }
         }
     }
@@ -150,40 +169,49 @@
     fun moveFocusAmongVisibleItems_userScrollIsOff() {
         // Arrange.
         rule.setTestContent {
-            lazyList(50.dp, lazyListState, userScrollEnabled = false) {
+            lazyList(50.dp, it, lazyListState, userScrollEnabled = false) {
                 item { FocusableBox(0) }
                 item { FocusableBox(1, initiallyFocused) }
                 item { FocusableBox(2) }
             }
         }
-        rule.runOnIdle { initiallyFocused.requestFocus() }
+        with(rule) {
+            forEachParameter(ParamsToRun) { param ->
+                runOnIdle { initiallyFocused.requestFocus() }
 
-        // Act.
-        val success = rule.runOnIdle {
-            focusManager.moveFocus(focusDirection)
-        }
-
-        // Assert.
-        rule.runOnIdle {
-            assertThat(success).apply { if (focusDirection == Enter) isFalse() else isTrue() }
-            when (focusDirection) {
-                Left -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 2 else 0]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 0 else 2]).isTrue()
+                // Act.
+                val success = runOnIdle {
+                    focusManager.moveFocus(focusDirection)
                 }
 
-                Right -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 0 else 2]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 2 else 0]).isTrue()
-                }
+                // Assert.
+                runOnIdle {
+                    assertThat(success).apply {
+                        if (focusDirection == Enter) isFalse() else isTrue()
+                    }
+                    when (focusDirection) {
+                        Left -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 2 else 0]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 0 else 2]).isTrue()
+                        }
 
-                Up -> assertThat(isFocused[if (reverseLayout) 2 else 0]).isTrue()
-                Down -> assertThat(isFocused[if (reverseLayout) 0 else 2]).isTrue()
-                Previous -> assertThat(isFocused[0]).isTrue()
-                Next -> assertThat(isFocused[2]).isTrue()
-                Enter -> assertThat(isFocused[1]).isTrue()
-                Exit -> assertThat(isLazyListFocused).isTrue()
-                else -> unsupportedDirection()
+                        Right -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 0 else 2]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 2 else 0]).isTrue()
+                        }
+
+                        Up -> assertThat(isFocused[if (param.reverseLayout) 2 else 0]).isTrue()
+                        Down -> assertThat(isFocused[if (param.reverseLayout) 0 else 2]).isTrue()
+                        Previous -> assertThat(isFocused[0]).isTrue()
+                        Next -> assertThat(isFocused[2]).isTrue()
+                        Enter -> assertThat(isFocused[1]).isTrue()
+                        Exit -> assertThat(isLazyListFocused).isTrue()
+                        else -> unsupportedDirection()
+                    }
+                }
+                runOnIdle { runBlocking { lazyListState.scrollToItem(0) } }
+                resetTestCase()
+                rule.waitForIdle()
             }
         }
     }
@@ -192,54 +220,65 @@
     fun moveFocusToItemThatIsJustBeyondBounds() {
         // Arrange.
         rule.setTestContent {
-            lazyList(30.dp, lazyListState) {
+            lazyList(30.dp, it, lazyListState) {
                 items(5) { FocusableBox(it) }
                 item { FocusableBox(5, initiallyFocused) }
                 items(5) { FocusableBox(it + 6) }
             }
         }
-        rule.runOnIdle {
-            // Scroll so that the focused item is in the middle.
-            runBlocking { lazyListState.scrollToItem(4) }
+        with(rule) {
+            forEachParameter(ParamsToRun) { param ->
+                runOnIdle {
+                    // Scroll so that the focused item is in the middle.
+                    runBlocking { lazyListState.scrollToItem(4) }
 
-            // Move focus to the last visible item.
-            initiallyFocused.requestFocus()
-            when (focusDirection) {
-                Left, Right, Up, Down, Previous, Next -> focusManager.moveFocus(focusDirection)
-                Enter, Exit -> {
-                    // Do nothing
+                    // Move focus to the last visible item.
+                    initiallyFocused.requestFocus()
+                    when (focusDirection) {
+                        Left, Right, Up, Down, Previous, Next -> focusManager.moveFocus(
+                            focusDirection
+                        )
+
+                        Enter, Exit -> {
+                            // Do nothing
+                        }
+
+                        else -> unsupportedDirection()
+                    }
                 }
 
-                else -> unsupportedDirection()
-            }
-        }
-
-        // Act.
-        val success = rule.runOnIdle {
-            focusManager.moveFocus(focusDirection)
-        }
-
-        // Assert.
-        rule.runOnIdle {
-            assertThat(success).apply { if (focusDirection == Enter) isFalse() else isTrue() }
-            when (focusDirection) {
-                Left -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 7 else 3]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 3 else 7]).isTrue()
+                // Act.
+                val success = runOnIdle {
+                    focusManager.moveFocus(focusDirection)
                 }
 
-                Right -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 3 else 7]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 7 else 3]).isTrue()
-                }
+                // Assert.
+                runOnIdle {
+                    assertThat(success).apply {
+                        if (focusDirection == Enter) isFalse() else isTrue()
+                    }
+                    when (focusDirection) {
+                        Left -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 7 else 3]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 3 else 7]).isTrue()
+                        }
 
-                Up -> assertThat(isFocused[if (reverseLayout) 7 else 3]).isTrue()
-                Down -> assertThat(isFocused[if (reverseLayout) 3 else 7]).isTrue()
-                Previous -> assertThat(isFocused[3]).isTrue()
-                Next -> assertThat(isFocused[7]).isTrue()
-                Enter -> assertThat(isFocused[5]).isTrue()
-                Exit -> assertThat(isLazyListFocused).isTrue()
-                else -> unsupportedDirection()
+                        Right -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 3 else 7]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 7 else 3]).isTrue()
+                        }
+
+                        Up -> assertThat(isFocused[if (param.reverseLayout) 7 else 3]).isTrue()
+                        Down -> assertThat(isFocused[if (param.reverseLayout) 3 else 7]).isTrue()
+                        Previous -> assertThat(isFocused[3]).isTrue()
+                        Next -> assertThat(isFocused[7]).isTrue()
+                        Enter -> assertThat(isFocused[5]).isTrue()
+                        Exit -> assertThat(isLazyListFocused).isTrue()
+                        else -> unsupportedDirection()
+                    }
+                }
+                runOnIdle { runBlocking { lazyListState.scrollToItem(0) } }
+                resetTestCase()
             }
         }
     }
@@ -248,44 +287,53 @@
     fun moveFocusToItemThatIsJustBeyondBounds_userScrollIsOff() {
         // Arrange.
         rule.setTestContent {
-            lazyList(30.dp, lazyListState, userScrollEnabled = false) {
+            lazyList(30.dp, it, lazyListState, userScrollEnabled = false) {
                 items(5) { FocusableBox(it) }
                 item { FocusableBox(5, initiallyFocused) }
                 items(5) { FocusableBox(it + 6) }
             }
         }
-        rule.runOnIdle {
-            // Scroll so that the focused item is in the middle.
-            runBlocking { lazyListState.scrollToItem(4) }
+        with(rule) {
+            forEachParameter(ParamsToRun) {
+                runOnIdle {
+                    // Scroll so that the focused item is in the middle.
+                    runBlocking { lazyListState.scrollToItem(4) }
 
-            // Move focus to the last visible item.
-            initiallyFocused.requestFocus()
-            when (focusDirection) {
-                Left, Right, Up, Down, Previous, Next -> focusManager.moveFocus(focusDirection)
-                Enter, Exit -> {
-                    // Do nothing
+                    // Move focus to the last visible item.
+                    initiallyFocused.requestFocus()
+                    when (focusDirection) {
+                        Left, Right, Up, Down, Previous, Next -> focusManager.moveFocus(
+                            focusDirection
+                        )
+
+                        Enter, Exit -> {
+                            // Do nothing
+                        }
+
+                        else -> unsupportedDirection()
+                    }
+                }
+                val firstVisibleItemIndex = lazyListState.firstVisibleItemIndex
+                // Act.
+                runOnIdle {
+                    focusManager.moveFocus(focusDirection)
                 }
 
-                else -> unsupportedDirection()
+                // Assert We Did Not Move
+                runOnIdle {
+                    assertThat(lazyListState.firstVisibleItemIndex).isEqualTo(firstVisibleItemIndex)
+                }
+                runOnIdle { runBlocking { lazyListState.scrollToItem(0) } }
+                resetTestCase()
             }
         }
-        val firstVisibleItemIndex = lazyListState.firstVisibleItemIndex
-        // Act.
-        rule.runOnIdle {
-            focusManager.moveFocus(focusDirection)
-        }
-
-        // Assert We Did Not Move
-        rule.runOnIdle {
-            assertThat(lazyListState.firstVisibleItemIndex).isEqualTo(firstVisibleItemIndex)
-        }
     }
 
     @Test
     fun moveFocusToItemThatIsFarBeyondBounds() {
         // Arrange.
         rule.setTestContent {
-            lazyList(30.dp, lazyListState) {
+            lazyList(30.dp, it, lazyListState) {
                 items(5) { FocusableBox(it) }
                 items(100) { Box(Modifier.size(10.dp)) }
                 item { FocusableBox(105) }
@@ -295,48 +343,65 @@
                 items(5) { FocusableBox(it + 208) }
             }
         }
-        rule.runOnIdle {
-            // Scroll so that the focused item is in the middle.
-            runBlocking { lazyListState.scrollToItem(105) }
-            initiallyFocused.requestFocus()
+        with(rule) {
+            forEachParameter(ParamsToRun) { param ->
+                runOnIdle {
+                    // Scroll so that the focused item is in the middle.
+                    runBlocking { lazyListState.scrollToItem(105) }
+                    initiallyFocused.requestFocus()
 
-            // Move focus to the last visible item.
-            when (focusDirection) {
-                Left, Right, Up, Down, Previous, Next -> focusManager.moveFocus(focusDirection)
-                Enter, Exit -> {
-                    // Do nothing
+                    // Move focus to the last visible item.
+                    when (focusDirection) {
+                        Left, Right, Up, Down, Previous, Next -> focusManager.moveFocus(
+                            focusDirection
+                        )
+
+                        Enter, Exit -> {
+                            // Do nothing
+                        }
+
+                        else -> unsupportedDirection()
+                    }
                 }
 
-                else -> unsupportedDirection()
-            }
-        }
-
-        // Act.
-        val success = rule.runOnIdle {
-            focusManager.moveFocus(focusDirection)
-        }
-
-        // Assert.
-        rule.runOnIdle {
-            assertThat(success).apply { if (focusDirection == Enter) isFalse() else isTrue() }
-            when (focusDirection) {
-                Left -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 208 else 4]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 4 else 208]).isTrue()
+                // Act.
+                val success = runOnIdle {
+                    focusManager.moveFocus(focusDirection)
                 }
 
-                Right -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 4 else 208]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 208 else 4]).isTrue()
-                }
+                // Assert.
+                runOnIdle {
+                    assertThat(success).apply {
+                        if (focusDirection == Enter) isFalse() else isTrue()
+                    }
+                    when (focusDirection) {
+                        Left -> when (param.layoutDirection) {
+                            Ltr ->
+                                assertThat(isFocused[if (param.reverseLayout) 208 else 4]).isTrue()
 
-                Up -> assertThat(isFocused[if (reverseLayout) 208 else 4]).isTrue()
-                Down -> assertThat(isFocused[if (reverseLayout) 4 else 208]).isTrue()
-                Previous -> assertThat(isFocused[4]).isTrue()
-                Next -> assertThat(isFocused[208]).isTrue()
-                Enter -> assertThat(isFocused[106]).isTrue()
-                Exit -> assertThat(isLazyListFocused).isTrue()
-                else -> unsupportedDirection()
+                            Rtl ->
+                                assertThat(isFocused[if (param.reverseLayout) 4 else 208]).isTrue()
+                        }
+
+                        Right -> when (param.layoutDirection) {
+                            Ltr ->
+                                assertThat(isFocused[if (param.reverseLayout) 4 else 208]).isTrue()
+
+                            Rtl ->
+                                assertThat(isFocused[if (param.reverseLayout) 208 else 4]).isTrue()
+                        }
+
+                        Up -> assertThat(isFocused[if (param.reverseLayout) 208 else 4]).isTrue()
+                        Down -> assertThat(isFocused[if (param.reverseLayout) 4 else 208]).isTrue()
+                        Previous -> assertThat(isFocused[4]).isTrue()
+                        Next -> assertThat(isFocused[208]).isTrue()
+                        Enter -> assertThat(isFocused[106]).isTrue()
+                        Exit -> assertThat(isLazyListFocused).isTrue()
+                        else -> unsupportedDirection()
+                    }
+                }
+                runOnIdle { runBlocking { lazyListState.scrollToItem(0) } }
+                resetTestCase()
             }
         }
     }
@@ -345,56 +410,77 @@
     fun moveFocusToItemThatIsBeyondBoundsAndInANestedLazyList() {
         // Arrange.
         rule.setTestContent {
-            lazyList(30.dp, lazyListState) {
-                item { lazyListCrossAxis(30.dp) { items(3) { FocusableBox(it + 0) } } }
+            lazyList(30.dp, it, lazyListState) {
+                item {
+                    lazyListCrossAxis(
+                        30.dp,
+                        it
+                    ) { items(3) { FocusableBox(it + 0) } }
+                }
                 item { FocusableBox(3) }
                 item { FocusableBox(4, initiallyFocused) }
                 item { FocusableBox(5) }
-                item { lazyListCrossAxis(30.dp) { items(3) { FocusableBox(it + 6) } } }
+                item {
+                    lazyListCrossAxis(
+                        30.dp,
+                        it
+                    ) { items(3) { FocusableBox(it + 6) } }
+                }
             }
         }
-        rule.runOnIdle {
-            // Scroll so that the focused item is in the middle.
-            runBlocking { lazyListState.scrollToItem(1) }
-            initiallyFocused.requestFocus()
+        with(rule) {
+            forEachParameter(ParamsToRun) { param ->
+                runOnIdle {
+                    // Scroll so that the focused item is in the middle.
+                    runBlocking { lazyListState.scrollToItem(1) }
+                    initiallyFocused.requestFocus()
 
-            // Move focus to the last visible item.
-            when (focusDirection) {
-                Left, Right, Up, Down, Previous, Next -> focusManager.moveFocus(focusDirection)
-                Enter, Exit -> {
-                    // Do nothing
+                    // Move focus to the last visible item.
+                    when (focusDirection) {
+                        Left, Right, Up, Down, Previous, Next -> focusManager.moveFocus(
+                            focusDirection
+                        )
+
+                        Enter, Exit -> {
+                            // Do nothing
+                        }
+
+                        else -> unsupportedDirection()
+                    }
                 }
 
-                else -> unsupportedDirection()
-            }
-        }
-
-        // Act.
-        val success = rule.runOnIdle {
-            focusManager.moveFocus(focusDirection)
-        }
-
-        // Assert.
-        rule.runOnIdle {
-            assertThat(success).apply { if (focusDirection == Enter) isFalse() else isTrue() }
-            when (focusDirection) {
-                Left -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 8 else 0]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 2 else 6]).isTrue()
+                // Act.
+                val success = runOnIdle {
+                    focusManager.moveFocus(focusDirection)
                 }
 
-                Right -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 2 else 6]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 8 else 0]).isTrue()
-                }
+                // Assert.
+                runOnIdle {
+                    assertThat(success).apply {
+                        if (focusDirection == Enter) isFalse() else isTrue()
+                    }
+                    when (focusDirection) {
+                        Left -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 8 else 0]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 2 else 6]).isTrue()
+                        }
 
-                Up -> assertThat(isFocused[if (reverseLayout) 8 else 0]).isTrue()
-                Down -> assertThat(isFocused[if (reverseLayout) 2 else 6]).isTrue()
-                Previous -> assertThat(isFocused[2]).isTrue()
-                Next -> assertThat(isFocused[6]).isTrue()
-                Enter -> assertThat(isFocused[4]).isTrue()
-                Exit -> assertThat(isLazyListFocused).isTrue()
-                else -> unsupportedDirection()
+                        Right -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 2 else 6]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 8 else 0]).isTrue()
+                        }
+
+                        Up -> assertThat(isFocused[if (param.reverseLayout) 8 else 0]).isTrue()
+                        Down -> assertThat(isFocused[if (param.reverseLayout) 2 else 6]).isTrue()
+                        Previous -> assertThat(isFocused[2]).isTrue()
+                        Next -> assertThat(isFocused[6]).isTrue()
+                        Enter -> assertThat(isFocused[4]).isTrue()
+                        Exit -> assertThat(isLazyListFocused).isTrue()
+                        else -> unsupportedDirection()
+                    }
+                }
+                runOnIdle { runBlocking { lazyListState.scrollToItem(0) } }
+                resetTestCase()
             }
         }
     }
@@ -407,57 +493,75 @@
 
         // Arrange.
         rule.setTestContent {
-            lazyList(30.dp, lazyListState) {
+            lazyList(30.dp, it, lazyListState) {
                 item { FocusableBox(0) }
-                item { lazyListCrossAxis(30.dp) { items(3) { FocusableBox(it + 1) } } }
+                item {
+                    lazyListCrossAxis(
+                        30.dp,
+                        it
+                    ) { items(3) { FocusableBox(it + 1) } }
+                }
                 item { FocusableBox(4, initiallyFocused) }
-                item { lazyListCrossAxis(30.dp) { items(3) { FocusableBox(it + 5) } } }
+                item {
+                    lazyListCrossAxis(
+                        30.dp,
+                        it
+                    ) { items(3) { FocusableBox(it + 5) } }
+                }
                 item { FocusableBox(8) }
             }
         }
-        rule.runOnIdle {
-            // Scroll so that the focused item is in the middle.
-            runBlocking { lazyListState.scrollToItem(1, 10) }
-            initiallyFocused.requestFocus()
+        with(rule) {
+            forEachParameter(ParamsToRun) { param ->
+                runOnIdle {
+                    // Scroll so that the focused item is in the middle.
+                    runBlocking { lazyListState.scrollToItem(1, 10) }
+                    initiallyFocused.requestFocus()
 
-            // Move focus to the last visible item.
-            when (focusDirection) {
-                Left, Right, Up, Down -> focusManager.moveFocus(focusDirection)
-                Previous, Next -> repeat(3) { focusManager.moveFocus(focusDirection) }
-                Enter, Exit -> {
-                    // Do nothing
+                    // Move focus to the last visible item.
+                    when (focusDirection) {
+                        Left, Right, Up, Down -> focusManager.moveFocus(focusDirection)
+                        Previous, Next -> repeat(3) { focusManager.moveFocus(focusDirection) }
+                        Enter, Exit -> {
+                            // Do nothing
+                        }
+
+                        else -> unsupportedDirection()
+                    }
                 }
 
-                else -> unsupportedDirection()
-            }
-        }
-
-        // Act.
-        val success = rule.runOnIdle {
-            focusManager.moveFocus(focusDirection)
-        }
-
-        // Assert.
-        rule.runOnIdle {
-            assertThat(success).apply { if (focusDirection == Enter) isFalse() else isTrue() }
-            when (focusDirection) {
-                Left -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 8 else 0]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 0 else 8]).isTrue()
+                // Act.
+                val success = runOnIdle {
+                    focusManager.moveFocus(focusDirection)
                 }
 
-                Right -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 0 else 8]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 8 else 0]).isTrue()
-                }
+                // Assert.
+                runOnIdle {
+                    assertThat(success).apply {
+                        if (focusDirection == Enter) isFalse() else isTrue()
+                    }
+                    when (focusDirection) {
+                        Left -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 8 else 0]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 0 else 8]).isTrue()
+                        }
 
-                Up -> assertThat(isFocused[if (reverseLayout) 8 else 0]).isTrue()
-                Down -> assertThat(isFocused[if (reverseLayout) 0 else 8]).isTrue()
-                Previous -> assertThat(isFocused[0]).isTrue()
-                Next -> assertThat(isFocused[8]).isTrue()
-                Enter -> assertThat(isFocused[4]).isTrue()
-                Exit -> assertThat(isLazyListFocused).isTrue()
-                else -> unsupportedDirection()
+                        Right -> when (param.layoutDirection) {
+                            Ltr -> assertThat(isFocused[if (param.reverseLayout) 0 else 8]).isTrue()
+                            Rtl -> assertThat(isFocused[if (param.reverseLayout) 8 else 0]).isTrue()
+                        }
+
+                        Up -> assertThat(isFocused[if (param.reverseLayout) 8 else 0]).isTrue()
+                        Down -> assertThat(isFocused[if (param.reverseLayout) 0 else 8]).isTrue()
+                        Previous -> assertThat(isFocused[0]).isTrue()
+                        Next -> assertThat(isFocused[8]).isTrue()
+                        Enter -> assertThat(isFocused[4]).isTrue()
+                        Exit -> assertThat(isLazyListFocused).isTrue()
+                        else -> unsupportedDirection()
+                    }
+                }
+                runOnIdle { runBlocking { lazyListState.scrollToItem(0) } }
+                resetTestCase()
             }
         }
     }
@@ -470,57 +574,91 @@
 
         // Arrange.
         rule.setTestContent {
-            lazyList(30.dp, lazyListState) {
-                item { lazyListCrossAxis(30.dp) { items(3) { FocusableBox(it + 0) } } }
-                item { lazyListCrossAxis(30.dp) { items(3) { FocusableBox(it + 3) } } }
+            lazyList(30.dp, it, lazyListState) {
+                item {
+                    lazyListCrossAxis(
+                        30.dp,
+                        it
+                    ) { items(3) { FocusableBox(it + 0) } }
+                }
+                item {
+                    lazyListCrossAxis(
+                        30.dp,
+                        it
+                    ) { items(3) { FocusableBox(it + 3) } }
+                }
                 item { FocusableBox(6, initiallyFocused) }
-                item { lazyListCrossAxis(30.dp) { items(3) { FocusableBox(it + 7) } } }
-                item { lazyListCrossAxis(30.dp) { items(3) { FocusableBox(it + 10) } } }
+                item {
+                    lazyListCrossAxis(
+                        30.dp,
+                        it
+                    ) { items(3) { FocusableBox(it + 7) } }
+                }
+                item {
+                    lazyListCrossAxis(
+                        30.dp,
+                        it
+                    ) { items(3) { FocusableBox(it + 10) } }
+                }
             }
         }
-        rule.runOnIdle {
-            // Scroll so that the focused item is in the middle.
-            runBlocking { lazyListState.scrollToItem(2, 0) }
-            initiallyFocused.requestFocus()
+        with(rule) {
+            forEachParameter(ParamsToRun) { param ->
+                runOnIdle {
+                    // Scroll so that the focused item is in the middle.
+                    runBlocking { lazyListState.scrollToItem(2, 0) }
+                    initiallyFocused.requestFocus()
 
-            // Move focus to the last visible item.
-            when (focusDirection) {
-                Left, Right, Up, Down -> focusManager.moveFocus(focusDirection)
-                Previous, Next -> repeat(3) { focusManager.moveFocus(focusDirection) }
-                Enter, Exit -> {
-                    // Do nothing
+                    // Move focus to the last visible item.
+                    when (focusDirection) {
+                        Left, Right, Up, Down -> focusManager.moveFocus(focusDirection)
+                        Previous, Next -> repeat(3) { focusManager.moveFocus(focusDirection) }
+                        Enter, Exit -> {
+                            // Do nothing
+                        }
+
+                        else -> unsupportedDirection()
+                    }
                 }
 
-                else -> unsupportedDirection()
-            }
-        }
-
-        // Act.
-        val success = rule.runOnIdle {
-            focusManager.moveFocus(focusDirection)
-        }
-
-        // Assert.
-        rule.runOnIdle {
-            assertThat(success).apply { if (focusDirection == Enter) isFalse() else isTrue() }
-            when (focusDirection) {
-                Left -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 12 else 0]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 2 else 10]).isTrue()
+                // Act.
+                val success = runOnIdle {
+                    focusManager.moveFocus(focusDirection)
                 }
 
-                Right -> when (layoutDirection) {
-                    Ltr -> assertThat(isFocused[if (reverseLayout) 2 else 10]).isTrue()
-                    Rtl -> assertThat(isFocused[if (reverseLayout) 12 else 0]).isTrue()
-                }
+                // Assert.
+                runOnIdle {
+                    assertThat(success).apply {
+                        if (focusDirection == Enter) isFalse() else isTrue()
+                    }
+                    when (focusDirection) {
+                        Left -> when (param.layoutDirection) {
+                            Ltr ->
+                                assertThat(isFocused[if (param.reverseLayout) 12 else 0]).isTrue()
 
-                Up -> assertThat(isFocused[if (reverseLayout) 12 else 0]).isTrue()
-                Down -> assertThat(isFocused[if (reverseLayout) 2 else 10]).isTrue()
-                Previous -> assertThat(isFocused[2]).isTrue()
-                Next -> assertThat(isFocused[10]).isTrue()
-                Enter -> assertThat(isFocused[6]).isTrue()
-                Exit -> assertThat(isLazyListFocused).isTrue()
-                else -> unsupportedDirection()
+                            Rtl ->
+                                assertThat(isFocused[if (param.reverseLayout) 2 else 10]).isTrue()
+                        }
+
+                        Right -> when (param.layoutDirection) {
+                            Ltr ->
+                                assertThat(isFocused[if (param.reverseLayout) 2 else 10]).isTrue()
+
+                            Rtl ->
+                                assertThat(isFocused[if (param.reverseLayout) 12 else 0]).isTrue()
+                        }
+
+                        Up -> assertThat(isFocused[if (param.reverseLayout) 12 else 0]).isTrue()
+                        Down -> assertThat(isFocused[if (param.reverseLayout) 2 else 10]).isTrue()
+                        Previous -> assertThat(isFocused[2]).isTrue()
+                        Next -> assertThat(isFocused[10]).isTrue()
+                        Enter -> assertThat(isFocused[6]).isTrue()
+                        Exit -> assertThat(isLazyListFocused).isTrue()
+                        else -> unsupportedDirection()
+                    }
+                }
+                runOnIdle { runBlocking { lazyListState.scrollToItem(0) } }
+                resetTestCase()
             }
         }
     }
@@ -536,12 +674,16 @@
         )
     }
 
-    private fun ComposeContentTestRule.setTestContent(composable: @Composable () -> Unit) {
+    private fun ParameterizedComposeTestRule<Param>.setTestContent(
+        composable: @Composable (param: Param) -> Unit
+    ) {
         setContent {
-            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
-                focusManager = LocalFocusManager.current
-                lazyListState = rememberLazyListState()
-                composable()
+            key(it) {
+                CompositionLocalProvider(LocalLayoutDirection provides it.layoutDirection) {
+                    focusManager = LocalFocusManager.current
+                    lazyListState = rememberLazyListState()
+                    composable(it)
+                }
             }
         }
     }
@@ -549,9 +691,10 @@
     @Composable
     private fun lazyList(
         size: Dp,
+        param: Param,
         state: LazyListState = rememberLazyListState(),
         userScrollEnabled: Boolean = true,
-        content: LazyListScope.() -> Unit
+        content: LazyListScope.() -> Unit,
     ) {
         when (focusDirection) {
             Left, Right, Enter, Exit, Next, Previous -> LazyRow(
@@ -560,7 +703,7 @@
                     .onFocusChanged { isLazyListFocused = it.isFocused }
                     .focusable(),
                 state = state,
-                reverseLayout = reverseLayout,
+                reverseLayout = param.reverseLayout,
                 content = content,
                 userScrollEnabled = userScrollEnabled
             )
@@ -571,7 +714,7 @@
                     .onFocusChanged { isLazyListFocused = it.isFocused }
                     .focusable(),
                 state = state,
-                reverseLayout = reverseLayout,
+                reverseLayout = param.reverseLayout,
                 content = content,
                 userScrollEnabled = userScrollEnabled
             )
@@ -583,6 +726,7 @@
     @Composable
     private fun lazyListCrossAxis(
         size: Dp,
+        param: Param,
         state: LazyListState = rememberLazyListState(),
         content: LazyListScope.() -> Unit
     ) {
@@ -590,14 +734,14 @@
             Left, Right, Enter, Exit, Next, Previous -> LazyColumn(
                 modifier = Modifier.size(size),
                 state = state,
-                reverseLayout = reverseLayout,
+                reverseLayout = param.reverseLayout,
                 content = content
             )
 
             Up, Down -> LazyRow(
                 modifier = Modifier.size(size),
                 state = state,
-                reverseLayout = reverseLayout,
+                reverseLayout = param.reverseLayout,
                 content = content
             )