1 /*
2 * Copyright (C) 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 com.android.test.input
18
19 import android.view.InputDevice.SOURCE_MOUSE
20 import android.view.InputDevice.SOURCE_STYLUS
21 import android.view.InputDevice.SOURCE_TOUCHPAD
22 import android.view.InputDevice.SOURCE_TOUCHSCREEN
23 import android.view.InputEventAssigner
24 import android.view.KeyEvent
25 import android.view.MotionEvent
26 import org.junit.Assert.assertEquals
27 import org.junit.Test
28
29 sealed class StreamEvent
30
31 private data object Vsync : StreamEvent()
32
33 data class MotionEventData(val action: Int, val source: Int, val id: Int, val expectedId: Int) :
34 StreamEvent()
35
36 /** Create a MotionEvent with the provided action, eventTime, and source */
createMotionEventnull37 fun createMotionEvent(action: Int, eventTime: Long, source: Int): MotionEvent {
38 val downTime: Long = 10
39 val x = 1f
40 val y = 2f
41 val pressure = 3f
42 val size = 1f
43 val metaState = 0
44 val xPrecision = 0f
45 val yPrecision = 0f
46 val deviceId = 1
47 val edgeFlags = 0
48 val displayId = 0
49 return MotionEvent.obtain(
50 downTime,
51 eventTime,
52 action,
53 x,
54 y,
55 pressure,
56 size,
57 metaState,
58 xPrecision,
59 yPrecision,
60 deviceId,
61 edgeFlags,
62 source,
63 displayId,
64 )
65 }
66
createKeyEventnull67 private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
68 val code = KeyEvent.KEYCODE_A
69 val repeat = 0
70 return KeyEvent(eventTime, eventTime, action, code, repeat)
71 }
72
73 /**
74 * Check that the correct eventIds are assigned in a stream. The stream consists of motion events or
75 * vsync (processed frame) Each streamEvent should have unique ids when writing tests The test
76 * passes even if two events get assigned the same eventId, since the mapping is streamEventId ->
77 * motionEventId and streamEvents have unique ids
78 */
checkEventStreamnull79 private fun checkEventStream(vararg streamEvents: StreamEvent) {
80 val assigner = InputEventAssigner()
81 var eventTime = 10L
82 // Maps MotionEventData.id to MotionEvent.id
83 // We can't control the event id of the generated motion events but for testing it's easier
84 // to label the events with a custom id for readability
85 val eventIdMap: HashMap<Int, Int> = HashMap()
86 for (streamEvent in streamEvents) {
87 when (streamEvent) {
88 is MotionEventData -> {
89 val event = createMotionEvent(streamEvent.action, eventTime, streamEvent.source)
90 eventIdMap[streamEvent.id] = event.id
91 val eventId = assigner.processEvent(event)
92 assertEquals(eventIdMap[streamEvent.expectedId], eventId)
93 }
94 is Vsync -> assigner.notifyFrameProcessed()
95 }
96 eventTime += 1
97 }
98 }
99
100 class InputEventAssignerTest {
101 companion object {
102 private const val TAG = "InputEventAssignerTest"
103 }
104
105 /** A single event should be assigned to the next available frame. */
106 @Test
testTouchMovenull107 fun testTouchMove() {
108 checkEventStream(
109 MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN, id = 1, expectedId = 1)
110 )
111 }
112
113 @Test
testMouseMovenull114 fun testMouseMove() {
115 checkEventStream(
116 MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_MOUSE, id = 1, expectedId = 1)
117 )
118 }
119
120 @Test
testMouseScrollnull121 fun testMouseScroll() {
122 checkEventStream(
123 MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 1, expectedId = 1)
124 )
125 }
126
127 @Test
testStylusMovenull128 fun testStylusMove() {
129 checkEventStream(
130 MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1)
131 )
132 }
133
134 @Test
testStylusHovernull135 fun testStylusHover() {
136 checkEventStream(
137 MotionEventData(MotionEvent.ACTION_HOVER_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1)
138 )
139 }
140
141 @Test
testTouchpadMovenull142 fun testTouchpadMove() {
143 checkEventStream(
144 MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1)
145 )
146 }
147
148 /**
149 * Test that before a VSYNC the event id generated by input event assigner for move events is
150 * the id of the down event. Move events coming after a VSYNC should be assigned their own event
151 * id
152 */
testDownAndMovenull153 private fun testDownAndMove(source: Int) {
154 checkEventStream(
155 MotionEventData(MotionEvent.ACTION_DOWN, source, id = 1, expectedId = 1),
156 MotionEventData(MotionEvent.ACTION_MOVE, source, id = 2, expectedId = 1),
157 Vsync,
158 MotionEventData(MotionEvent.ACTION_MOVE, source, id = 4, expectedId = 4),
159 )
160 }
161
162 @Test
testTouchDownAndMovenull163 fun testTouchDownAndMove() {
164 testDownAndMove(SOURCE_TOUCHSCREEN)
165 }
166
167 @Test
testMouseDownAndMovenull168 fun testMouseDownAndMove() {
169 testDownAndMove(SOURCE_MOUSE)
170 }
171
172 @Test
testStylusDownAndMovenull173 fun testStylusDownAndMove() {
174 testDownAndMove(SOURCE_STYLUS)
175 }
176
177 @Test
testTouchpadDownAndMovenull178 fun testTouchpadDownAndMove() {
179 testDownAndMove(SOURCE_TOUCHPAD)
180 }
181
182 /** After an up event, motion events should be assigned their own event id */
183 @Test
testMouseDownUpAndScrollnull184 fun testMouseDownUpAndScroll() {
185 checkEventStream(
186 MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1),
187 MotionEventData(MotionEvent.ACTION_UP, SOURCE_MOUSE, id = 2, expectedId = 2),
188 MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3),
189 )
190 }
191
192 /** After an up event, motion events should be assigned their own event id */
193 @Test
testStylusDownUpAndHovernull194 fun testStylusDownUpAndHover() {
195 checkEventStream(
196 MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1),
197 MotionEventData(MotionEvent.ACTION_UP, SOURCE_STYLUS, id = 2, expectedId = 2),
198 MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3),
199 )
200 }
201
202 /** After a cancel event, motion events should be assigned their own event id */
203 @Test
testMouseDownCancelAndScrollnull204 fun testMouseDownCancelAndScroll() {
205 checkEventStream(
206 MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1),
207 MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_MOUSE, id = 2, expectedId = 2),
208 MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3),
209 )
210 }
211
212 /** After a cancel event, motion events should be assigned their own event id */
213 @Test
testStylusDownCancelAndHovernull214 fun testStylusDownCancelAndHover() {
215 checkEventStream(
216 MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1),
217 MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_STYLUS, id = 2, expectedId = 2),
218 MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3),
219 )
220 }
221
222 /** KeyEvents are processed immediately, so the latest event should be returned. */
223 @Test
testKeyEventnull224 fun testKeyEvent() {
225 val assigner = InputEventAssigner()
226 val down = createKeyEvent(KeyEvent.ACTION_DOWN, 20)
227 var eventId = assigner.processEvent(down)
228 assertEquals(down.id, eventId)
229 val up = createKeyEvent(KeyEvent.ACTION_UP, 21)
230 eventId = assigner.processEvent(up)
231 // DOWN is only sticky for Motions, not for keys
232 assertEquals(up.id, eventId)
233 assigner.notifyFrameProcessed()
234 val down2 = createKeyEvent(KeyEvent.ACTION_DOWN, 22)
235 eventId = assigner.processEvent(down2)
236 assertEquals(down2.id, eventId)
237 }
238 }
239