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

Commit 79655f21 authored by Paul Ramirez's avatar Paul Ramirez
Browse files

Add test coverage to InputConsumerNoResampling

Implemented tests that check for multiple samples in a single
batch

Bug: 297226446
Flag: TEST_ONLY
Test: TEST=libinput_tests; m $TEST &&
$ANDROID_HOST_OUT/nativetest64/$TEST/$TEST
--gtest_filter="*InputPublisherAndConsumerNoResamplingTest*"

Change-Id: Ib34010048225f4f66e6380d05fd4df75fa7fa810
parent 0c25e864
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ namespace android {
/**
 * An interface to receive batched input events. Even if you don't want batching, you still have to
 * use this interface, and some of the events will be batched if your implementation is slow to
 * handle the incoming input.
 * handle the incoming input. The events received by these callbacks are never null.
 */
class InputConsumerCallbacks {
public:
+124 −1
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ struct PublishMotionArgs {
    const int32_t action;
    const nsecs_t downTime;
    const uint32_t seq;
    const int32_t eventId;
    int32_t eventId;
    const int32_t deviceId = 1;
    const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
    const ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT;
@@ -291,6 +291,7 @@ protected:
    void publishAndConsumeKeyEvent();
    void publishAndConsumeMotionStream();
    void publishAndConsumeMotionDown(nsecs_t downTime);
    void publishAndConsumeSinglePointerMultipleSamples(const size_t nSamples);
    void publishAndConsumeBatchedMotionMove(nsecs_t downTime);
    void publishAndConsumeFocusEvent();
    void publishAndConsumeCaptureEvent();
@@ -298,6 +299,7 @@ protected:
    void publishAndConsumeTouchModeEvent();
    void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime,
                                      const std::vector<Pointer>& pointers);

    void TearDown() override {
        // Destroy the consumer, flushing any of the pending ack's.
        sendMessage(LooperMessage::DESTROY_CONSUMER);
@@ -519,6 +521,123 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionDown(nsec
                                 {Pointer{.id = 0, .x = 20, .y = 30}});
}

/*
 * Decompose a potential multi-sampled MotionEvent into multiple MotionEvents
 * with a single sample.
 */
std::vector<MotionEvent> splitBatchedMotionEvent(const MotionEvent& batchedMotionEvent) {
    std::vector<MotionEvent> singleMotionEvents;
    const size_t batchSize = batchedMotionEvent.getHistorySize() + 1;
    for (size_t i = 0; i < batchSize; ++i) {
        MotionEvent singleMotionEvent;
        singleMotionEvent
                .initialize(batchedMotionEvent.getId(), batchedMotionEvent.getDeviceId(),
                            batchedMotionEvent.getSource(), batchedMotionEvent.getDisplayId(),
                            batchedMotionEvent.getHmac(), batchedMotionEvent.getAction(),
                            batchedMotionEvent.getActionButton(), batchedMotionEvent.getFlags(),
                            batchedMotionEvent.getEdgeFlags(), batchedMotionEvent.getMetaState(),
                            batchedMotionEvent.getButtonState(),
                            batchedMotionEvent.getClassification(),
                            batchedMotionEvent.getTransform(), batchedMotionEvent.getXPrecision(),
                            batchedMotionEvent.getYPrecision(),
                            batchedMotionEvent.getRawXCursorPosition(),
                            batchedMotionEvent.getRawYCursorPosition(),
                            batchedMotionEvent.getRawTransform(), batchedMotionEvent.getDownTime(),
                            batchedMotionEvent.getHistoricalEventTime(/*historicalIndex=*/i),
                            batchedMotionEvent.getPointerCount(),
                            batchedMotionEvent.getPointerProperties(),
                            (batchedMotionEvent.getSamplePointerCoords() + i));
        singleMotionEvents.push_back(singleMotionEvent);
    }
    return singleMotionEvents;
}

/*
 * Simulates a single pointer touching the screen and leaving it there for a period of time.
 * Publishes a DOWN event and consumes it right away. Then, publishes a sequence of MOVE
 * samples for the same pointer, and waits until it has been consumed. Splits batched MotionEvents
 * into individual samples. Checks the consumed MotionEvents against the published ones.
 * This test is non-deterministic because it depends on the timing of arrival of events to the
 * socket.
 *
 * @param nSamples The number of MOVE samples to publish before attempting consumption.
 */
void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMultipleSamples(
        const size_t nSamples) {
    const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
    const Pointer pointer(0, 20, 30);

    const PublishMotionArgs argsDown(AMOTION_EVENT_ACTION_DOWN, downTime, {pointer}, mSeq);
    const nsecs_t publishTimeOfDown = systemTime(SYSTEM_TIME_MONOTONIC);
    publishMotionEvent(*mPublisher, argsDown);

    // Consume the DOWN event.
    ASSERT_TRUE(mMotionEvents.popWithTimeout(TIMEOUT).has_value());

    verifyFinishedSignal(*mPublisher, mSeq, publishTimeOfDown);

    std::vector<nsecs_t> publishTimes;
    std::vector<PublishMotionArgs> argsMoves;
    std::queue<uint32_t> publishedSequenceNumbers;

    // Block Looper to increase the chance of batching events
    {
        std::scoped_lock l(mLock);
        mLooperMayProceed = false;
    }
    sendMessage(LooperMessage::BLOCK_LOOPER);
    {
        std::unique_lock l(mLock);
        mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; });
    }

    uint32_t firstSampleId;
    for (size_t i = 0; i < nSamples; ++i) {
        publishedSequenceNumbers.push(++mSeq);
        PublishMotionArgs argsMove(AMOTION_EVENT_ACTION_MOVE, downTime, {pointer}, mSeq);
        // A batched MotionEvent only has a single event id, currently determined when the
        // MotionEvent is initialized. Therefore, to pass the eventId comparisons inside
        // verifyArgsEqualToEvent, we need to override the event id of the published args to match
        // the event id of the first sample inside the MotionEvent.
        if (i == 0) {
            firstSampleId = argsMove.eventId;
        }
        argsMove.eventId = firstSampleId;
        publishTimes.push_back(systemTime(SYSTEM_TIME_MONOTONIC));
        argsMoves.push_back(argsMove);
        publishMotionEvent(*mPublisher, argsMove);
    }

    std::vector<MotionEvent> singleSampledMotionEvents;

    // Unblock Looper
    {
        std::scoped_lock l(mLock);
        mLooperMayProceed = true;
    }
    mNotifyLooperMayProceed.notify_all();

    // We have no control over the socket behavior, so the consumer can receive
    // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a
    // mix of those)
    while (singleSampledMotionEvents.size() != nSamples) {
        const std::optional<std::unique_ptr<MotionEvent>> batchedMotionEvent =
                mMotionEvents.popWithTimeout(TIMEOUT);
        // The events received by these calls are never null
        std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(**batchedMotionEvent);
        singleSampledMotionEvents.insert(singleSampledMotionEvents.end(), splitMotionEvents.begin(),
                                         splitMotionEvents.end());
    }

    // Consumer can choose to finish events in any order. For simplicity,
    // we verify the events in sequence (since that is how the test is implemented).
    for (size_t i = 0; i < nSamples; ++i) {
        verifyArgsEqualToEvent(argsMoves[i], singleSampledMotionEvents[i]);
        verifyFinishedSignal(*mPublisher, publishedSequenceNumbers.front(), publishTimes[i]);
        publishedSequenceNumbers.pop();
    }
}

void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMove(
        nsecs_t downTime) {
    uint32_t seq = mSeq++;
@@ -814,4 +933,8 @@ TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMultipleEvents_EndToEnd
    ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
}

TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishAndConsumeSinglePointer) {
    publishAndConsumeSinglePointerMultipleSamples(3);
}

} // namespace android