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

Commit 7d04c4bc authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Add unit tests for external stylus fusion

Add unit tests to demystify the external stylus fusion process and to
document its current behavior.

A fused external stylus reports pressure through a separate input
device. That pressure information is then fused with touches from the
touchscreen.

Bug: 246394583
Test: atest inputflinger_tests
Change-Id: I289e4470eb9383bc9e203bff8f76609b0d533e51
parent 8f8d251a
Loading
Loading
Loading
Loading
+1 −8
Original line number Diff line number Diff line
@@ -31,13 +31,6 @@ namespace android {

// --- Constants ---

// Maximum amount of latency to add to touch events while waiting for data from an
// external stylus.
static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);

// Maximum amount of time to wait on touch data before pushing out new pressure data.
static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);

// Artificial latency on synthetic events created from stylus data without corresponding touch
// data.
static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
@@ -1760,7 +1753,7 @@ std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) {
            out += dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
        }
    } else if (mDeviceMode == DeviceMode::DIRECT) {
        if (mExternalStylusFusionTimeout < when) {
        if (mExternalStylusFusionTimeout <= when) {
            out += processRawTouches(true /*timeout*/);
        } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+7 −0
Original line number Diff line number Diff line
@@ -27,6 +27,13 @@

namespace android {

// Maximum amount of latency to add to touch events while waiting for data from an
// external stylus.
static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);

// Maximum amount of time to wait on touch data before pushing out new pressure data.
static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);

/* Raw axis information from the driver. */
struct RawPointerAxes {
    RawAbsoluteAxisInfo x{};
+391 −0
Original line number Diff line number Diff line
@@ -1343,6 +1343,8 @@ protected:
        int32_t mGlobalMetaState;
        bool mUpdateGlobalMetaStateWasCalled;
        int32_t mGeneration;
        std::optional<nsecs_t> mRequestedTimeout;
        std::vector<InputDeviceInfo> mExternalStylusDevices;
    public:
        FakeInputReaderContext(InputReader* reader)
@@ -1376,6 +1378,29 @@ protected:
            mGeneration = ContextImpl::bumpGeneration();
            return mGeneration;
        }
        void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
        void assertTimeoutWasRequested(nsecs_t when) {
            ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
                                           << " but there was no timeout requested.";
            ASSERT_EQ(when, *mRequestedTimeout);
            mRequestedTimeout.reset();
        }
        void assertTimeoutWasNotRequested() {
            ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
                                               " but one was requested at time "
                                            << *mRequestedTimeout;
        }
        void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
            outDevices = mExternalStylusDevices;
        }
        void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
            mExternalStylusDevices = devices;
        }
    } mFakeContext;
    friend class InputReaderTest;
@@ -3365,6 +3390,16 @@ protected:
        mReader->loopOnce();
    }
    std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when) {
        std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
        for (const NotifyArgs& args : generatedArgs) {
            mFakeListener->notify(args);
        }
        // Loop the reader to flush the input listener queue.
        mReader->loopOnce();
        return generatedArgs;
    }
    static void assertMotionRange(const InputDeviceInfo& info,
            int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
        const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
@@ -7459,6 +7494,362 @@ TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
    }
}
// --- ExternalStylusFusionTest ---
class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
public:
    SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
        addConfigurationProperty("touch.deviceType", "touchScreen");
        prepareDisplay(DISPLAY_ORIENTATION_0);
        prepareButtons();
        prepareAxes(POSITION);
        auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
        mStylusState.when = ARBITRARY_TIME;
        mStylusState.pressure = 0.f;
        mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
        mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo});
        configureDevice(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
        processExternalStylusState(mapper);
        return mapper;
    }
    std::list<NotifyArgs> processExternalStylusState(InputMapper& mapper) {
        std::list<NotifyArgs> generatedArgs = mapper.updateExternalStylusState(mStylusState);
        for (const NotifyArgs& args : generatedArgs) {
            mFakeListener->notify(args);
        }
        // Loop the reader to flush the input listener queue.
        mReader->loopOnce();
        return generatedArgs;
    }
protected:
    StylusState mStylusState{};
    static constexpr uint32_t EXPECTED_SOURCE =
            AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_BLUETOOTH_STYLUS;
    void testStartFusedStylusGesture(SingleTouchInputMapper& mapper) {
        auto toolTypeSource =
                AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
        // The first pointer is withheld.
        processDown(mapper, 100, 200);
        processSync(mapper);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
        ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
                ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
        // The external stylus reports pressure. The withheld finger pointer is released as a
        // stylus.
        mStylusState.pressure = 1.f;
        processExternalStylusState(mapper);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
                AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
        ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
        // Subsequent pointer events are not withheld.
        processMove(mapper, 101, 201);
        processSync(mapper);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
                AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
        ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    }
    void testSuccessfulFusionGesture(SingleTouchInputMapper& mapper) {
        ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper));
        // Releasing the touch pointer ends the gesture.
        processUp(mapper);
        processSync(mapper);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE),
                      WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
        mStylusState.pressure = 0.f;
        processExternalStylusState(mapper);
        ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    }
    void testUnsuccessfulFusionGesture(SingleTouchInputMapper& mapper) {
        auto toolTypeSource =
                AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER));
        // The first pointer is withheld when an external stylus is connected,
        // and a timeout is requested.
        processDown(mapper, 100, 200);
        processSync(mapper);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
        ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
                ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
        // If the timeout expires early, it is requested again.
        handleTimeout(mapper, ARBITRARY_TIME + 1);
        ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
                ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
        // When the timeout expires, the withheld touch is released as a finger pointer.
        handleTimeout(mapper, ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
                AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
        // Subsequent pointer events are not withheld.
        processMove(mapper, 101, 201);
        processSync(mapper);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
                AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
        processUp(mapper);
        processSync(mapper);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
                AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP))));
        ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    }
private:
    InputDeviceInfo mExternalStylusDeviceInfo{};
};
TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) {
    SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
    ASSERT_EQ(EXPECTED_SOURCE, mapper.getSources());
}
TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) {
    SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
    ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
}
TEST_F(ExternalStylusFusionTest, SuccessfulFusion_TouchFirst) {
    SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
    ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
}
// Test a successful stylus fusion gesture where the pressure is reported by the external
// before the touch is reported by the touchscreen.
TEST_F(ExternalStylusFusionTest, SuccessfulFusion_PressureFirst) {
    SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
    auto toolTypeSource =
            AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
    // The external stylus reports pressure first. It is ignored for now.
    mStylusState.pressure = 1.f;
    processExternalStylusState(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    // When the touch goes down afterwards, it is reported as a stylus pointer.
    processDown(mapper, 100, 200);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    processMove(mapper, 101, 201);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
    processUp(mapper);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
TEST_F(ExternalStylusFusionTest, FusionIsRepeatedForEachNewGesture) {
    SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
    ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
    ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
    ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
    ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
    ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
    ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
}
TEST_F(ExternalStylusFusionTest, FusedPointerReportsPressureChanges) {
    SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
    auto toolTypeSource =
            AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
    mStylusState.pressure = 0.8f;
    processExternalStylusState(mapper);
    processDown(mapper, 100, 200);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
                  WithPressure(0.8f))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    // The external stylus reports a pressure change. We wait for some time for a touch event.
    mStylusState.pressure = 0.6f;
    processExternalStylusState(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    ASSERT_NO_FATAL_FAILURE(
            mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
    // If a touch is reported within the timeout, it reports the updated pressure.
    processMove(mapper, 101, 201);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                  WithPressure(0.6f))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    // There is another pressure change.
    mStylusState.pressure = 0.5f;
    processExternalStylusState(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    ASSERT_NO_FATAL_FAILURE(
            mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
    // If a touch is not reported within the timeout, a move event is generated to report
    // the new pressure.
    handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                  WithPressure(0.5f))));
    // If a zero pressure is reported before the touch goes up, the previous pressure value is
    // repeated indefinitely.
    mStylusState.pressure = 0.0f;
    processExternalStylusState(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    ASSERT_NO_FATAL_FAILURE(
            mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
    processMove(mapper, 102, 202);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                  WithPressure(0.5f))));
    processMove(mapper, 103, 203);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                  WithPressure(0.5f))));
    processUp(mapper);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE),
                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
TEST_F(ExternalStylusFusionTest, FusedPointerReportsToolTypeChanges) {
    SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
    auto source = WithSource(EXPECTED_SOURCE);
    mStylusState.pressure = 1.f;
    mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_ERASER;
    processExternalStylusState(mapper);
    processDown(mapper, 100, 200);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
                  WithToolType(AMOTION_EVENT_TOOL_TYPE_ERASER))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    // The external stylus reports a tool change. We wait for some time for a touch event.
    mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
    processExternalStylusState(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    ASSERT_NO_FATAL_FAILURE(
            mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
    // If a touch is reported within the timeout, it reports the updated pressure.
    processMove(mapper, 101, 201);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    // There is another tool type change.
    mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
    processExternalStylusState(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    ASSERT_NO_FATAL_FAILURE(
            mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
    // If a touch is not reported within the timeout, a move event is generated to report
    // the new tool type.
    handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                  WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
    processUp(mapper);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_UP),
                  WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
TEST_F(ExternalStylusFusionTest, FusedPointerReportsButtons) {
    SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
    auto toolTypeSource =
            AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
    ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper));
    // The external stylus reports a button change. We wait for some time for a touch event.
    mStylusState.buttons = AMOTION_EVENT_BUTTON_STYLUS_PRIMARY;
    processExternalStylusState(mapper);
    ASSERT_NO_FATAL_FAILURE(
            mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
    // If a touch is reported within the timeout, it reports the updated button state.
    processMove(mapper, 101, 201);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    // The button is now released.
    mStylusState.buttons = 0;
    processExternalStylusState(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    ASSERT_NO_FATAL_FAILURE(
            mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
    // If a touch is not reported within the timeout, a move event is generated to report
    // the new button state.
    handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
    // TODO(prabirmsp): Fix fused stylus button releases being handled inconsistently.
    //   The button release event should be sent here, but isn't.
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
    processUp(mapper);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
                  WithButtonState(0))));
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
// --- MultiTouchInputMapperTest ---
class MultiTouchInputMapperTest : public TouchInputMapperTest {
+2 −2
Original line number Diff line number Diff line
@@ -70,8 +70,8 @@ MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {

MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
    const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
    *result_listener << "expected pressure " << pressure << ", but got " << pressure;
    return argPressure;
    *result_listener << "expected pressure " << pressure << ", but got " << argPressure;
    return argPressure == pressure;
}

MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {