Add VelocityTracker Benchmark tests for Planar Axes

Bug: 268413323
Test: atest VelocityTrackerBenchmarkTest
Change-Id: I359b7916fc28f534c7ff0805fd9d15765976c02a
diff --git a/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt b/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
index 324fcc6..13269eb 100644
--- a/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
+++ b/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
@@ -23,33 +23,133 @@
 import androidx.test.filters.LargeTest
 import androidx.test.runner.AndroidJUnit4
 
+import java.time.Duration
+
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
-private fun createScrollMotionEvent(scrollAmount: Float, eventTimeMs: Long): MotionEvent {
-    val props = MotionEvent.PointerProperties()
-    props.id = 0
-    val coords = MotionEvent.PointerCoords()
-    coords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAmount)
-    return MotionEvent.obtain(
-        /*downTime=*/0,
-        eventTimeMs,
-        MotionEvent.ACTION_SCROLL,
-        /*pointerCount=*/1,
-        arrayOf(props),
-        arrayOf(coords),
-        /*metaState=*/0,
-        /*buttonState=*/0,
-        /*xPrecision=*/0f,
-        /*yPrecision=*/0f,
-        /*deviceId=*/1,
-        /*edgeFlags=*/0,
-        InputDevice.SOURCE_ROTARY_ENCODER,
-        /*flags=*/0
-    )
+/**
+ * Helper class to maintain [MotionEvent]s for tests.
+ *
+ * This is primarily used to create [MotionEvent]s for tests, in a way where a sequence of
+ * [MotionEvent]s created in multiple test runs are exactly the same, as long as [reset] is called
+ * between consecutive sequences of [MotionEvent]s.
+ *
+ * Furthermore, it also contains convenience methods to run any queries/verifications of the
+ * generated [MotionEvent]s.
+ */
+abstract class MotionState {
+    /** Current time, in ms. */
+    protected var currentTime = START_TIME
+
+    /** Resets the state of this instance. */
+    open fun reset() {
+        currentTime = START_TIME
+    }
+
+    /** Creates a [MotionEvent]. */
+    abstract fun createMotionEvent(): MotionEvent
+
+    /** Asserts that the current velocity is not zero, just for verifying there's motion. */
+    abstract fun assertNonZeroVelocity(velocityTracker: VelocityTracker)
+
+    companion object {
+        /** Arbitrarily chosen start time. */
+        val START_TIME = Duration.ofMillis(100)
+        /**
+         * A small enough time jump, which won't be considered by the tracker as big enough to
+         * deduce that a pointer has stopped.
+         */
+        val DEFAULT_TIME_JUMP = Duration.ofMillis(2)
+    }
+}
+
+/** An implementation of [MotionState] for [MotionEvent.AXIS_SCROLL]. */
+private class ScrollMotionState : MotionState() {
+    override fun createMotionEvent(): MotionEvent {
+        val props = MotionEvent.PointerProperties()
+        props.id = 0
+        val coords = MotionEvent.PointerCoords()
+        coords.setAxisValue(MotionEvent.AXIS_SCROLL, DEFAULT_SCROLL_AMOUNT)
+        val motionEvent = MotionEvent.obtain(
+            /*downTime=*/0,
+            currentTime.toMillis(),
+            MotionEvent.ACTION_SCROLL,
+            /*pointerCount=*/1,
+            arrayOf(props),
+            arrayOf(coords),
+            /*metaState=*/0,
+            /*buttonState=*/0,
+            /*xPrecision=*/0f,
+            /*yPrecision=*/0f,
+            /*deviceId=*/1,
+            /*edgeFlags=*/0,
+            InputDevice.SOURCE_ROTARY_ENCODER,
+            /*flags=*/0
+        )
+
+        currentTime = currentTime.plus(DEFAULT_TIME_JUMP)
+
+        return motionEvent
+    }
+
+    override fun assertNonZeroVelocity(velocityTracker: VelocityTracker) {
+        Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_SCROLL) != 0f)
+    }
+
+    companion object {
+        private val DEFAULT_SCROLL_AMOUNT: Float = 30f
+    }
+}
+
+/** An implementation of [MotionState] for [MotionEvent.AXIS_X] and [MotionEvent.AXIS_Y]. */
+private class PlanarMotionState : MotionState() {
+    private var x: Float = DEFAULT_X
+    private var y: Float = DEFAULT_Y
+    private var downEventCreated = false
+
+    override fun createMotionEvent(): MotionEvent {
+        val action: Int = if (downEventCreated) MotionEvent.ACTION_MOVE else MotionEvent.ACTION_DOWN
+        val motionEvent = MotionEvent.obtain(
+            /*downTime=*/START_TIME.toMillis(),
+            currentTime.toMillis(),
+            action,
+            x,
+            y,
+            /*metaState=*/0)
+
+        if (downEventCreated) {
+            x += INCREMENT
+            y += INCREMENT
+        } else {
+            downEventCreated = true
+        }
+        currentTime = currentTime.plus(DEFAULT_TIME_JUMP)
+
+        return motionEvent
+    }
+
+    override fun assertNonZeroVelocity(velocityTracker: VelocityTracker) {
+        Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_X) != 0f)
+        Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_Y) != 0f)
+    }
+
+    override fun reset() {
+        super.reset()
+        x = DEFAULT_X
+        y = DEFAULT_Y
+        downEventCreated = false
+    }
+
+    companion object {
+        /** Arbitrarily chosen constants. No need to have varying velocity for now. */
+        private val DEFAULT_X: Float = 2f
+        private val DEFAULT_Y: Float = 4f
+        private val INCREMENT: Float = 0.7f
+    }
 }
 
 /**
@@ -72,65 +172,80 @@
 
     @Test
     fun addMovement_axisScroll() {
+        testAddMovement(ScrollMotionState())
+    }
+
+    @Test
+    fun computeCurrentVelocity_computeAfterAllAdditions_axisScroll() {
+        testComputeCurrentVelocity_computeAfterAllAdditions(ScrollMotionState())
+    }
+
+    @Test
+    fun computeCurrentVelocity_computeAfterEachAdd_axisScroll() {
+        testComputeCurrentVelocity_computeAfterEachAdd(ScrollMotionState())
+    }
+
+    @Test
+    fun addMovementTest_planarAxes() {
+        testAddMovement(PlanarMotionState())
+    }
+
+    @Test
+    fun computeCurrentVelocity_computeAfterAllAdditions_planarAxes() {
+        testComputeCurrentVelocity_computeAfterAllAdditions(PlanarMotionState())
+    }
+
+    private fun testAddMovement(motionState: MotionState) {
         val state = perfStatusReporter.getBenchmarkState()
         while (state.keepRunning()) {
             state.pauseTiming()
-            var eventTimeMs: Long = 100
-            val scrollAmount = 30f
             for (i in 0 until TEST_NUM_DATAPOINTS) {
+                val motionEvent = motionState.createMotionEvent()
                 state.resumeTiming()
-                velocityTracker.addMovement(createScrollMotionEvent(scrollAmount, eventTimeMs))
+                velocityTracker.addMovement(motionEvent)
                 state.pauseTiming()
-                eventTimeMs += 2
             }
             velocityTracker.computeCurrentVelocity(1000)
-            Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_SCROLL) > 0)
+            motionState.assertNonZeroVelocity(velocityTracker)
             // Clear the tracker for the next run
             velocityTracker.clear()
+            motionState.reset()
             state.resumeTiming()
         }
     }
 
-    @Test
-    fun computeCurrentVelocity_constantVelocity_axisScroll_computeAfterAllAdditions() {
+    private fun testComputeCurrentVelocity_computeAfterAllAdditions(motionState: MotionState) {
         val state = perfStatusReporter.getBenchmarkState()
         while (state.keepRunning()) {
             // Add the data points
             state.pauseTiming()
-            var eventTimeMs: Long = 100
-            val scrollAmount = 30f
             for (i in 0 until TEST_NUM_DATAPOINTS) {
-                velocityTracker.addMovement(createScrollMotionEvent(scrollAmount, eventTimeMs))
-                eventTimeMs += 2
+                velocityTracker.addMovement(motionState.createMotionEvent())
             }
 
             // Do the velocity computation
             state.resumeTiming()
             velocityTracker.computeCurrentVelocity(1000)
 
-            // Clear the tracker for the next run
             state.pauseTiming()
-            Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_SCROLL) > 0)
+            motionState.assertNonZeroVelocity(velocityTracker)
+            // Clear the tracker for the next run
             velocityTracker.clear()
             state.resumeTiming()
         }
     }
 
-    @Test
-    fun computeCurrentVelocity_constantVelocity_axisScroll_computeAfterEachAdd() {
+    private fun testComputeCurrentVelocity_computeAfterEachAdd(motionState: MotionState) {
         val state = perfStatusReporter.getBenchmarkState()
         while (state.keepRunning()) {
             state.pauseTiming()
-            var eventTimeMs: Long = 100
-            val scrollAmount = 30f
             for (i in 0 until TEST_NUM_DATAPOINTS) {
-                velocityTracker.addMovement(createScrollMotionEvent(scrollAmount, eventTimeMs))
+                velocityTracker.addMovement(motionState.createMotionEvent())
                 state.resumeTiming()
                 velocityTracker.computeCurrentVelocity(1000)
                 state.pauseTiming()
-                eventTimeMs += 2
             }
-            Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_SCROLL) > 0)
+            motionState.assertNonZeroVelocity(velocityTracker)
             // Clear the tracker for the next run
             velocityTracker.clear()
             state.resumeTiming()