VirtualTouchpad_test.cpp revision 06936feb07bf6fbef262e4adebeb8e53910d8cf4
1#include <android/input.h>
2#include <gtest/gtest.h>
3#include <linux/input.h>
4#include <cstdio>
5#include <cstdlib>
6#include <cstring>
7
8#include "EvdevInjector.h"
9#include "VirtualTouchpadEvdev.h"
10
11namespace android {
12namespace dvr {
13
14namespace {
15
16class UInputForTesting : public EvdevInjector::UInput {
17 public:
18  ~UInputForTesting() override {}
19  void WriteInputEvent(uint16_t type, uint16_t code, int32_t value) {
20    struct input_event event;
21    memset(&event, 0, sizeof(event));
22    event.type = type;
23    event.code = code;
24    event.value = value;
25    Write(&event, sizeof(event));
26  }
27};
28
29// Recording test implementation of UInput.
30//
31class UInputRecorder : public UInputForTesting {
32 public:
33  UInputRecorder() {}
34  ~UInputRecorder() override {}
35
36  const std::string& GetString() const { return s_; }
37  void Reset() { s_.clear(); }
38
39  // UInput overrides:
40
41  int Open() override {
42    s_ += "o;";
43    return 0;
44  }
45
46  int Close() override {
47    s_ += "c;";
48    return 0;
49  }
50
51  int Write(const void* buf, size_t count) override {
52    s_ += "w(";
53    s_ += Encode(&count, sizeof(count));
54    s_ += ",";
55    s_ += Encode(buf, count);
56    s_ += ");";
57    return 0;
58  }
59
60  int IoctlVoid(int request) override {
61    s_ += "i(";
62    s_ += Encode(&request, sizeof(request));
63    s_ += ");";
64    return 0;
65  }
66
67  int IoctlSetInt(int request, int value) override {
68    s_ += "i(";
69    s_ += Encode(&request, sizeof(request));
70    s_ += ",";
71    s_ += Encode(&value, sizeof(value));
72    s_ += ");";
73    return 0;
74  }
75
76 private:
77  std::string s_;
78
79  std::string Encode(const void* buf, size_t count) {
80    const char* in = static_cast<const char*>(buf);
81    char out[2 * count + 1];
82    for (size_t i = 0; i < count; ++i) {
83      snprintf(&out[2 * i], 3, "%02X", in[i]);
84    }
85    return out;
86  }
87};
88
89class EvdevInjectorForTesting : public EvdevInjector {
90 public:
91  EvdevInjectorForTesting() { SetUInputForTesting(&record); }
92  const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); }
93  UInputRecorder record;
94};
95
96class VirtualTouchpadForTesting : public VirtualTouchpadEvdev {
97 public:
98  static std::unique_ptr<VirtualTouchpad> Create() {
99    return std::unique_ptr<VirtualTouchpad>(New());
100  }
101  static VirtualTouchpadForTesting* New() {
102    VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting();
103    touchpad->Reset();
104    for (int t = 0; t < kTouchpads; ++t) {
105      touchpad->SetEvdevInjectorForTesting(t, &touchpad->injector[t]);
106    }
107    return touchpad;
108  }
109  int GetTouchpadCount() const { return kTouchpads; }
110  EvdevInjectorForTesting injector[kTouchpads];
111};
112
113void DumpDifference(const char* expect, const char* actual) {
114  printf("  common: ");
115  while (*expect && *expect == *actual) {
116    putchar(*expect);
117    ++expect;
118    ++actual;
119  }
120  printf("\n  expect: %s\n", expect);
121  printf("  actual: %s\n", actual);
122}
123
124}  // anonymous namespace
125
126class VirtualTouchpadTest : public testing::Test {};
127
128TEST_F(VirtualTouchpadTest, Goodness) {
129  std::unique_ptr<VirtualTouchpadForTesting> touchpad(
130      VirtualTouchpadForTesting::New());
131  UInputRecorder expect;
132
133  status_t touch_status = touchpad->Attach();
134  EXPECT_EQ(0, touch_status);
135
136  // Check some aspects of uinput_user_dev.
137  const uinput_user_dev* uidev;
138  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
139    SCOPED_TRACE(t);
140    uidev = touchpad->injector[t].GetUiDev();
141    String8 name;
142    name.appendFormat("vr-virtual-touchpad-%d", t);
143    EXPECT_EQ(name, uidev->name);
144    for (int i = 0; i < ABS_CNT; ++i) {
145      EXPECT_EQ(0, uidev->absmin[i]);
146      EXPECT_EQ(0, uidev->absfuzz[i]);
147      EXPECT_EQ(0, uidev->absflat[i]);
148      if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y &&
149          i != ABS_MT_SLOT) {
150        EXPECT_EQ(0, uidev->absmax[i]);
151      }
152    }
153  }
154  const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X];
155  const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y];
156  const int32_t slots = uidev->absmax[ABS_MT_SLOT];
157
158  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
159    SCOPED_TRACE(t);
160    // Check the system calls performed by initialization.
161    expect.Reset();
162    // From ConfigureBegin():
163    expect.Open();
164    // From ConfigureInputProperty(INPUT_PROP_DIRECT):
165    expect.IoctlSetInt(UI_SET_PROPBIT, INPUT_PROP_DIRECT);
166    // From ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1):
167    expect.IoctlSetInt(UI_SET_EVBIT, EV_ABS);
168    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_X);
169    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_Y);
170    // From ConfigureAbsSlots(kSlots):
171    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_SLOT);
172    // From ConfigureKey(BTN_TOUCH):
173    expect.IoctlSetInt(UI_SET_EVBIT, EV_KEY);
174    expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
175    expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK);
176    // From ConfigureEnd():
177    expect.Write(touchpad->injector[t].GetUiDev(), sizeof(uinput_user_dev));
178    expect.IoctlVoid(UI_DEV_CREATE);
179    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
180  }
181
182  expect.Reset();
183  expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0);
184  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
185  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0);
186  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0);
187  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
188  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
189    SCOPED_TRACE(t);
190    touchpad->injector[t].record.Reset();
191    touch_status = touchpad->Touch(t, 0, 0, 0);
192    EXPECT_EQ(0, touch_status);
193    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
194  }
195
196  expect.Reset();
197  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
198  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.25f * width);
199  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.75f * height);
200  expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_PRESS);
201  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
202  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
203    SCOPED_TRACE(t);
204    touchpad->injector[t].record.Reset();
205    touch_status = touchpad->Touch(t, 0.25f, 0.75f, 0.5f);
206    EXPECT_EQ(0, touch_status);
207    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
208  }
209
210  expect.Reset();
211  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
212  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.99f * width);
213  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.99f * height);
214  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
215  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
216    SCOPED_TRACE(t);
217    touchpad->injector[t].record.Reset();
218    touch_status = touchpad->Touch(t, 0.99f, 0.99f, 0.99f);
219    EXPECT_EQ(0, touch_status);
220    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
221  }
222
223  expect.Reset();
224  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
225    SCOPED_TRACE(t);
226    touchpad->injector[t].record.Reset();
227    touch_status = touchpad->Touch(t, 1.0f, 1.0f, 1.0f);
228    EXPECT_EQ(EINVAL, touch_status);
229    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
230  }
231
232  expect.Reset();
233  expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_RELEASE);
234  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
235  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
236  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
237    SCOPED_TRACE(t);
238    touchpad->injector[t].record.Reset();
239    touch_status = touchpad->Touch(t, 0.25f, 0.75f, -0.01f);
240    EXPECT_EQ(0, touch_status);
241    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
242  }
243
244  expect.Reset();
245  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS);
246  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
247  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
248    SCOPED_TRACE(t);
249    touchpad->injector[t].record.Reset();
250    touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
251    EXPECT_EQ(0, touch_status);
252    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
253  }
254
255  expect.Reset();
256  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
257    SCOPED_TRACE(t);
258    touchpad->injector[t].record.Reset();
259    touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
260    EXPECT_EQ(0, touch_status);
261    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
262  }
263
264  expect.Reset();
265  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE);
266  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
267  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
268    SCOPED_TRACE(t);
269    touchpad->injector[t].record.Reset();
270    touch_status = touchpad->ButtonState(t, 0);
271    EXPECT_EQ(0, touch_status);
272    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
273  }
274
275  expect.Reset();
276  expect.Close();
277  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
278    SCOPED_TRACE(t);
279    touchpad->injector[t].record.Reset();
280  }
281  touch_status = touchpad->Detach();
282  EXPECT_EQ(0, touch_status);
283  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
284    SCOPED_TRACE(t);
285    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
286  }
287}
288
289TEST_F(VirtualTouchpadTest, Badness) {
290  std::unique_ptr<VirtualTouchpadForTesting> touchpad(
291      VirtualTouchpadForTesting::New());
292  UInputRecorder expect;
293  UInputRecorder& record = touchpad->injector[VirtualTouchpad::PRIMARY].record;
294
295  status_t touch_status = touchpad->Attach();
296  EXPECT_EQ(0, touch_status);
297
298  // Touch off-screen should return an error,
299  // and should not result in any system calls.
300  expect.Reset();
301  record.Reset();
302  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f);
303  EXPECT_NE(OK, touch_status);
304  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f);
305  EXPECT_NE(OK, touch_status);
306  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.25f, 0.75f, 1.0f);
307  EXPECT_NE(OK, touch_status);
308  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 1.75f, 1.0f);
309  EXPECT_NE(OK, touch_status);
310  EXPECT_EQ(expect.GetString(), record.GetString());
311
312  // Unsupported button should return an error,
313  // and should not result in any system calls.
314  expect.Reset();
315  record.Reset();
316  touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY,
317                                       AMOTION_EVENT_BUTTON_FORWARD);
318  EXPECT_NE(OK, touch_status);
319  EXPECT_EQ(expect.GetString(), record.GetString());
320
321  // Repeated attach is an error.
322  touch_status = touchpad->Attach();
323  EXPECT_NE(0, touch_status);
324}
325
326}  // namespace dvr
327}  // namespace android
328