1 /*
<lambda>null2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.ui.test.injectionscope.touch
18 
19 import androidx.compose.foundation.layout.Box
20 import androidx.compose.foundation.layout.fillMaxSize
21 import androidx.compose.ui.Modifier
22 import androidx.compose.ui.geometry.Offset
23 import androidx.compose.ui.test.TouchInjectionScope
24 import androidx.compose.ui.test.junit4.createComposeRule
25 import androidx.compose.ui.test.onNodeWithTag
26 import androidx.compose.ui.test.performTouchInput
27 import androidx.compose.ui.test.swipe
28 import androidx.compose.ui.test.util.ClickableTestBox
29 import androidx.compose.ui.test.util.SinglePointerInputRecorder
30 import androidx.compose.ui.test.util.areSampledFromCurve
31 import androidx.compose.ui.test.util.assertOnlyLastEventIsUp
32 import androidx.compose.ui.test.util.assertSinglePointer
33 import androidx.compose.ui.test.util.assertUpSameAsLastMove
34 import androidx.compose.ui.test.util.downEvents
35 import androidx.compose.ui.test.util.hasSameTimeBetweenEvents
36 import androidx.compose.ui.test.util.recordedDurationMillis
37 import androidx.test.filters.MediumTest
38 import com.google.common.truth.Truth.assertThat
39 import org.junit.Before
40 import org.junit.Rule
41 import org.junit.Test
42 import org.junit.runner.RunWith
43 import org.junit.runners.Parameterized
44 
45 /** Test for [TouchInjectionScope.swipe] along a curve with key times */
46 @MediumTest
47 @RunWith(Parameterized::class)
48 class SwipeCurveWithKeyTimesTest(private val config: TestConfig) {
49     data class TestConfig(val keyTimes: List<Long>)
50 
51     companion object {
52         private const val tag = "widget"
53         private const val duration = 100L
54 
55         @JvmStatic
56         @Parameterized.Parameters(name = "{0}")
57         fun createTestSet(): List<TestConfig> =
58             listOf(
59                 TestConfig(emptyList()),
60                 TestConfig(listOf(0)),
61                 TestConfig(listOf(1)),
62                 TestConfig(listOf(50)),
63                 TestConfig(listOf(51)),
64                 TestConfig(listOf(duration - 1)),
65                 TestConfig(listOf(duration)),
66                 TestConfig(listOf(33, 66)),
67                 TestConfig(listOf(45, 46, 47)),
68                 TestConfig(listOf(45, 55, 65)),
69             )
70     }
71 
72     @get:Rule val rule = createComposeRule()
73 
74     private val recorder = SinglePointerInputRecorder()
75 
76     private fun curve(t: Long) = Offset(t + 10f, t + 10f)
77 
78     @Before
79     fun setContent() {
80         rule.setContent {
81             Box(Modifier.fillMaxSize()) { ClickableTestBox(modifier = recorder, tag = tag) }
82         }
83     }
84 
85     @Test
86     fun swipe() {
87         rule.onNodeWithTag(tag).performTouchInput {
88             swipe(curve = ::curve, duration, config.keyTimes)
89         }
90 
91         rule.runOnIdle {
92             recorder.apply {
93                 assertThat(events.size).isAtLeast(3)
94                 assertOnlyLastEventIsUp()
95                 assertUpSameAsLastMove()
96                 assertSinglePointer()
97 
98                 val t0 = events[0].timestamp
99 
100                 // All key times have been sampled
101                 assertThat(events.map { it.timestamp - t0 })
102                     .containsAtLeastElementsIn(config.keyTimes)
103 
104                 // The duration of the gesture is as expected
105                 assertThat(recordedDurationMillis).isEqualTo(duration)
106                 // And each event is sampled from the curve
107                 downEvents.areSampledFromCurve(::curve)
108 
109                 // All events between two key times are evenly spaced in time
110                 (listOf(0L) + config.keyTimes + listOf(duration))
111                     .distinct()
112                     .zipWithNext { a, b -> downEvents.filter { (it.timestamp - t0) in a..b } }
113                     .forEach { it.hasSameTimeBetweenEvents() }
114             }
115         }
116     }
117 }
118