• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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