blob: 198ed0611b6f96b0c4f4da7439d9b5fba5f52bb4 [file] [log] [blame]
#include <android/input.h>
#include <gtest/gtest.h>
#include <linux/input.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "EvdevInjector.h"
#include "VirtualTouchpadEvdev.h"
namespace android {
namespace dvr {
namespace {
class UInputForTesting : public EvdevInjector::UInput {
public:
~UInputForTesting() override {}
void WriteInputEvent(uint16_t type, uint16_t code, int32_t value) {
struct input_event event;
memset(&event, 0, sizeof(event));
event.type = type;
event.code = code;
event.value = value;
Write(&event, sizeof(event));
}
};
// Recording test implementation of UInput.
//
class UInputRecorder : public UInputForTesting {
public:
UInputRecorder() {}
~UInputRecorder() override {}
const std::string& GetString() const { return s_; }
void Reset() { s_.clear(); }
// UInput overrides:
int Open() override {
s_ += "o;";
return 0;
}
int Close() override {
s_ += "c;";
return 0;
}
int Write(const void* buf, size_t count) override {
s_ += "w(";
s_ += Encode(&count, sizeof(count));
s_ += ",";
s_ += Encode(buf, count);
s_ += ");";
return 0;
}
int IoctlVoid(int request) override {
s_ += "i(";
s_ += Encode(&request, sizeof(request));
s_ += ");";
return 0;
}
int IoctlSetInt(int request, int value) override {
s_ += "i(";
s_ += Encode(&request, sizeof(request));
s_ += ",";
s_ += Encode(&value, sizeof(value));
s_ += ");";
return 0;
}
private:
std::string s_;
std::string Encode(const void* buf, size_t count) {
const char* in = static_cast<const char*>(buf);
char out[2 * count + 1];
for (size_t i = 0; i < count; ++i) {
snprintf(&out[2 * i], 3, "%02X", in[i]);
}
return out;
}
};
class EvdevInjectorForTesting : public EvdevInjector {
public:
EvdevInjectorForTesting() { SetUInputForTesting(&record); }
const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); }
UInputRecorder record;
};
class VirtualTouchpadForTesting : public VirtualTouchpadEvdev {
public:
static std::unique_ptr<VirtualTouchpad> Create() {
return std::unique_ptr<VirtualTouchpad>(New());
}
static VirtualTouchpadForTesting* New() {
VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting();
touchpad->Reset();
for (int t = 0; t < kTouchpads; ++t) {
touchpad->SetEvdevInjectorForTesting(t, &touchpad->injector[t]);
}
return touchpad;
}
int GetTouchpadCount() const { return kTouchpads; }
EvdevInjectorForTesting injector[kTouchpads];
};
} // anonymous namespace
class VirtualTouchpadTest : public testing::Test {};
TEST_F(VirtualTouchpadTest, Goodness) {
std::unique_ptr<VirtualTouchpadForTesting> touchpad(
VirtualTouchpadForTesting::New());
UInputRecorder expect;
status_t touch_status = touchpad->Attach();
EXPECT_EQ(0, touch_status);
// Check some aspects of uinput_user_dev.
const uinput_user_dev* uidev;
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
uidev = touchpad->injector[t].GetUiDev();
String8 name;
name.appendFormat("vr-virtual-touchpad-%d", t);
EXPECT_EQ(name, uidev->name);
for (int i = 0; i < ABS_CNT; ++i) {
EXPECT_EQ(0, uidev->absmin[i]);
EXPECT_EQ(0, uidev->absfuzz[i]);
EXPECT_EQ(0, uidev->absflat[i]);
if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y &&
i != ABS_MT_SLOT) {
EXPECT_EQ(0, uidev->absmax[i]);
}
}
}
const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X];
const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y];
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
// Check the system calls performed by initialization.
expect.Reset();
// From ConfigureBegin():
expect.Open();
// From ConfigureInputProperty(INPUT_PROP_DIRECT):
expect.IoctlSetInt(UI_SET_PROPBIT, INPUT_PROP_DIRECT);
// From ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1):
expect.IoctlSetInt(UI_SET_EVBIT, EV_ABS);
expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_X);
expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_Y);
// From ConfigureAbsSlots(kSlots):
expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_SLOT);
// From ConfigureRel(REL_WHEEL):
expect.IoctlSetInt(UI_SET_EVBIT, EV_REL);
expect.IoctlSetInt(UI_SET_RELBIT, REL_WHEEL);
// From ConfigureRel(REL_HWHEEL):
expect.IoctlSetInt(UI_SET_RELBIT, REL_HWHEEL);
// From ConfigureKey(BTN_TOUCH):
expect.IoctlSetInt(UI_SET_EVBIT, EV_KEY);
expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK);
// From ConfigureEnd():
expect.Write(touchpad->injector[t].GetUiDev(), sizeof(uinput_user_dev));
expect.IoctlVoid(UI_DEV_CREATE);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 0, 0, 0);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.25f * width);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.75f * height);
expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_PRESS);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 0.25f, 0.75f, 0.5f);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.99f * width);
expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.99f * height);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 0.99f, 0.99f, 0.99f);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 1.0f, 1.0f, 1.0f);
EXPECT_EQ(EINVAL, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_RELEASE);
expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->Touch(t, 0.25f, 0.75f, -0.01f);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE);
expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
touch_status = touchpad->ButtonState(t, 0);
EXPECT_EQ(0, touch_status);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
expect.Reset();
expect.Close();
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
touchpad->injector[t].record.Reset();
}
touch_status = touchpad->Detach();
EXPECT_EQ(0, touch_status);
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
}
}
TEST_F(VirtualTouchpadTest, Badness) {
std::unique_ptr<VirtualTouchpadForTesting> touchpad(
VirtualTouchpadForTesting::New());
UInputRecorder expect;
UInputRecorder& record = touchpad->injector[VirtualTouchpad::PRIMARY].record;
status_t touch_status = touchpad->Attach();
EXPECT_EQ(0, touch_status);
// Touch off-screen should return an error,
// and should not result in any system calls.
expect.Reset();
record.Reset();
touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f);
EXPECT_NE(OK, touch_status);
touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f);
EXPECT_NE(OK, touch_status);
touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.25f, 0.75f, 1.0f);
EXPECT_NE(OK, touch_status);
touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 1.75f, 1.0f);
EXPECT_NE(OK, touch_status);
EXPECT_EQ(expect.GetString(), record.GetString());
// Unsupported button should return an error,
// and should not result in any system calls.
expect.Reset();
record.Reset();
touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY,
AMOTION_EVENT_BUTTON_FORWARD);
EXPECT_NE(OK, touch_status);
EXPECT_EQ(expect.GetString(), record.GetString());
// Repeated attach is an error.
touch_status = touchpad->Attach();
EXPECT_NE(0, touch_status);
}
} // namespace dvr
} // namespace android