Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 067d914b authored by Yeabkal Wubshit's avatar Yeabkal Wubshit
Browse files

Add VelocityTracker Benchmark tests for Planar Axes

Bug: 268413323
Test: atest VelocityTrackerBenchmarkTest
Change-Id: I359b7916fc28f534c7ff0805fd9d15765976c02a
parent 273488b3
Loading
Loading
Loading
Loading
+156 −41
Original line number Diff line number Diff line
@@ -23,20 +23,60 @@ import android.view.VelocityTracker
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 {
/**
 * 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, scrollAmount)
    return MotionEvent.obtain(
        coords.setAxisValue(MotionEvent.AXIS_SCROLL, DEFAULT_SCROLL_AMOUNT)
        val motionEvent = MotionEvent.obtain(
            /*downTime=*/0,
        eventTimeMs,
            currentTime.toMillis(),
            MotionEvent.ACTION_SCROLL,
            /*pointerCount=*/1,
            arrayOf(props),
@@ -50,6 +90,66 @@ private fun createScrollMotionEvent(scrollAmount: Float, eventTimeMs: Long): Mot
            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 @@ class VelocityTrackerBenchmarkTest {

    @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()