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

Commit aab25629 authored by Arthur Hung's avatar Arthur Hung
Browse files

Add TouchIntegrationTest tests

Base on InputReaderIntegrationTest which would test the threading
functionality. This would emulate the touch screen device and
send the raw events to verify if InputListener could really
receive the MotionEvent.

- Add tests for handling single touch and multi touch.
- Add test for handling MT_TOOL_PALM.

Bug: 117933934
Test: atest TouchIntegrationTest
Change-Id: I450c0159ac60d7da0471235407b3c537ab94fa6f
parent 85f33da3
Loading
Loading
Loading
Loading
+130 −2
Original line number Diff line number Diff line
@@ -1748,7 +1748,7 @@ protected:

    virtual void SetUp() override {
        mFakePolicy = new FakeInputReaderPolicy();
        mTestListener = new TestInputListener();
        mTestListener = new TestInputListener(50ms);

        mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener);
        ASSERT_EQ(mReader->start(), OK);
@@ -1847,6 +1847,135 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) {
    ASSERT_LE(prevTimestamp, keyArgs.eventTime);
}

// --- TouchProcessTest ---
class TouchIntegrationTest : public InputReaderIntegrationTest {
protected:
    static const int32_t FIRST_SLOT = 0;
    static const int32_t SECOND_SLOT = 1;
    static const int32_t FIRST_TRACKING_ID = 0;
    static const int32_t SECOND_TRACKING_ID = 1;
    const std::string UNIQUE_ID = "local:0";

    virtual void SetUp() override {
        InputReaderIntegrationTest::SetUp();
        // At least add an internal display.
        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
                                     ViewportType::VIEWPORT_INTERNAL);

        mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
    }

    void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
                                      int32_t orientation, const std::string& uniqueId,
                                      std::optional<uint8_t> physicalPort,
                                      ViewportType viewportType) {
        mFakePolicy->addDisplayViewport(displayId, width, height, orientation, uniqueId,
                                        physicalPort, viewportType);
        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
    }

    std::unique_ptr<UinputTouchScreen> mDevice;
};

TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
    NotifyMotionArgs args;
    const Point centerPoint = mDevice->getCenterPoint();

    // ACTION_DOWN
    mDevice->sendDown(centerPoint);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);

    // ACTION_MOVE
    mDevice->sendMove(centerPoint + Point(1, 1));
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);

    // ACTION_UP
    mDevice->sendUp();
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}

TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
    NotifyMotionArgs args;
    const Point centerPoint = mDevice->getCenterPoint();

    // ACTION_DOWN
    mDevice->sendDown(centerPoint);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);

    // ACTION_POINTER_DOWN (Second slot)
    const Point secondPoint = centerPoint + Point(100, 100);
    mDevice->sendSlot(SECOND_SLOT);
    mDevice->sendTrackingId(SECOND_TRACKING_ID);
    mDevice->sendDown(secondPoint + Point(1, 1));
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
              args.action);

    // ACTION_MOVE (Second slot)
    mDevice->sendMove(secondPoint);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);

    // ACTION_POINTER_UP (Second slot)
    mDevice->sendUp();
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
              args.action);

    // ACTION_UP
    mDevice->sendSlot(FIRST_SLOT);
    mDevice->sendUp();
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}

TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
    NotifyMotionArgs args;
    const Point centerPoint = mDevice->getCenterPoint();

    // ACTION_DOWN
    mDevice->sendDown(centerPoint);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);

    // ACTION_POINTER_DOWN (Second slot)
    const Point secondPoint = centerPoint + Point(100, 100);
    mDevice->sendSlot(SECOND_SLOT);
    mDevice->sendTrackingId(SECOND_TRACKING_ID);
    mDevice->sendDown(secondPoint);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
              args.action);

    // ACTION_MOVE (Second slot)
    mDevice->sendMove(secondPoint + Point(1, 1));
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);

    // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event.
    // Expect to receive ACTION_CANCEL, to abort the entire gesture.
    mDevice->sendToolType(MT_TOOL_PALM);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);

    // ACTION_POINTER_UP (Second slot)
    mDevice->sendUp();

    // ACTION_UP
    mDevice->sendSlot(FIRST_SLOT);
    mDevice->sendUp();

    // Expect no event received after abort the entire gesture.
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
}

// --- InputDeviceTest ---
class InputDeviceTest : public testing::Test {
protected:
@@ -7032,5 +7161,4 @@ TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) {
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}

} // namespace android
+5 −14
Original line number Diff line number Diff line
@@ -19,20 +19,11 @@

#include "TestInputListener.h"

namespace {

using std::chrono_literals::operator""ms;

// Timeout for waiting for an expected event
static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms;

} // namespace

namespace android {

// --- TestInputListener ---

TestInputListener::TestInputListener() { }
TestInputListener::TestInputListener(const std::chrono::milliseconds timeout) : mTimeout(timeout) {}

TestInputListener::~TestInputListener() { }

@@ -95,9 +86,9 @@ void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string m

    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
    if (queue.empty()) {
        const bool eventReceived =
                mCondition.wait_for(lock, WAIT_TIMEOUT,
                                    [&queue]() REQUIRES(mLock) { return !queue.empty(); });
        const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) {
            return !queue.empty();
        });
        if (!eventReceived) {
            FAIL() << "Timed out waiting for event: " << message.c_str();
        }
@@ -114,7 +105,7 @@ void TestInputListener::assertNotCalled(std::string message) {
    base::ScopedLockAssertion assumeLocked(mLock);

    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
    const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) {
    const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) {
        return !queue.empty();
    });
    if (eventReceived) {
+4 −1
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include <gtest/gtest.h>
#include "InputListener.h"

using std::chrono_literals::operator""ms;

namespace android {

// --- TestInputListener ---
@@ -30,7 +32,7 @@ protected:
    virtual ~TestInputListener();

public:
    TestInputListener();
    TestInputListener(const std::chrono::milliseconds timeout = 5ms);

    void assertNotifyConfigurationChangedWasCalled(
            NotifyConfigurationChangedArgs* outEventArgs = nullptr);
@@ -73,6 +75,7 @@ private:

    std::mutex mLock;
    std::condition_variable mCondition;
    const std::chrono::milliseconds mTimeout;

    std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
               std::vector<NotifyDeviceResetArgs>,          //
+68 −1
Original line number Diff line number Diff line
@@ -127,4 +127,71 @@ void UinputHomeKey::pressAndReleaseHomeKey() {
    EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME));
}

// --- UinputTouchScreen ---
UinputTouchScreen::UinputTouchScreen(const Rect* size)
      : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {}

void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
    // Setup the touch screen device
    ioctl(fd, UI_SET_EVBIT, EV_KEY);
    ioctl(fd, UI_SET_EVBIT, EV_REL);
    ioctl(fd, UI_SET_EVBIT, EV_ABS);
    ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
    ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
    ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
    ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
    ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);

    device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
    device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
    device->absmin[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MIN;
    device->absmax[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MAX;
    device->absmin[ABS_MT_POSITION_X] = mSize.left;
    device->absmax[ABS_MT_POSITION_X] = mSize.right - 1;
    device->absmin[ABS_MT_POSITION_Y] = mSize.top;
    device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
    device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
    device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
}

void UinputTouchScreen::sendSlot(int32_t slot) {
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_SLOT, slot));
}

void UinputTouchScreen::sendTrackingId(int32_t trackingId) {
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId));
}

void UinputTouchScreen::sendDown(const Point& point) {
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 1));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
}

void UinputTouchScreen::sendMove(const Point& point) {
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
}

void UinputTouchScreen::sendUp() {
    sendTrackingId(0xffffffff);
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 0));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
}

void UinputTouchScreen::sendToolType(int32_t toolType) {
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
}

// Get the center x, y base on the range definition.
const Point UinputTouchScreen::getCenterPoint() {
    return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2);
}

} // namespace android
+36 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@
#include <inttypes.h>
#include <linux/uinput.h>
#include <log/log.h>
#include <ui/Point.h>
#include <ui/Rect.h>

#include <memory>

@@ -106,6 +108,40 @@ private:
    UinputHomeKey();
};

// --- UinputTouchScreen ---
// A touch screen device with specific size.
class UinputTouchScreen : public UinputDevice {
public:
    static constexpr const char* DEVICE_NAME = "Test Touch Screen";
    static const int32_t RAW_TOUCH_MIN = 0;
    static const int32_t RAW_TOUCH_MAX = 31;
    static const int32_t RAW_ID_MIN = 0;
    static const int32_t RAW_ID_MAX = 9;
    static const int32_t RAW_SLOT_MIN = 0;
    static const int32_t RAW_SLOT_MAX = 9;
    static const int32_t RAW_PRESSURE_MIN = 0;
    static const int32_t RAW_PRESSURE_MAX = 255;

    template <class D, class... Ts>
    friend std::unique_ptr<D> createUinputDevice(Ts... args);

    void sendSlot(int32_t slot);
    void sendTrackingId(int32_t trackingId);
    void sendDown(const Point& point);
    void sendMove(const Point& point);
    void sendUp();
    void sendToolType(int32_t toolType);

    const Point getCenterPoint();

protected:
    UinputTouchScreen(const Rect* size);

private:
    void configureDevice(int fd, uinput_user_dev* device) override;
    const Rect mSize;
};

} // namespace android

#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H