1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <cctype>
17 #include <gtest/gtest.h>
18 #include <thread>
19 
20 #include "mmi_log.h"
21 #include "virtual_pen.h"
22 
23 #undef MMI_LOG_TAG
24 #define MMI_LOG_TAG "TransformPointTest"
25 
26 namespace OHOS {
27 namespace MMI {
28 namespace {
29 using namespace testing::ext;
30 constexpr int32_t WAIT_TIME_FOR_INPUT { 1000 };
31 constexpr int32_t WAIT_TIME_FOR_EVENTS { 10 };
32 constexpr size_t DEFAULT_BUF_SIZE { 4096 };
33 } // namespace
34 
35 #define ARRAY_LENGTH(arr)   (sizeof(arr) / sizeof((arr)[0]))
36 
37 class Context {
38 public:
39     explicit Context(const std::string& node);
40     ~Context();
41     bool IsReady() const;
42     int GetFd() const;
43 
44 private:
45     std::string node_;
46     int fd_ { -1 };
47 };
48 
Context(const std::string & node)49 Context::Context(const std::string& node)
50     : node_(node)
51 {
52     MMI_HILOGD("Open device node: \'%{public}s\'.", node_.c_str());
53     fd_ = open(node_.c_str(), O_RDWR);
54     if (fd_ < 0) {
55         MMI_HILOGE("Failed to open device node: \'%{public}s\'.", node_.c_str());
56     }
57 }
58 
~Context()59 Context::~Context()
60 {
61     if (fd_ >= 0) {
62         close(fd_);
63     }
64 }
65 
IsReady() const66 inline bool Context::IsReady() const
67 {
68     return (fd_ >= 0);
69 }
70 
GetFd() const71 inline int Context::GetFd() const
72 {
73     return fd_;
74 }
75 
76 class TransformPointTest : public testing::Test {
77 public:
78     static void SetUpTestCase(void);
79     static void TearDownTestCase(void);
80     static std::string GetDeviceNodeName();
81     static int Execute(const std::string& command, std::vector<std::string>& results);
82     static void GetInputDeviceNodes(std::map<std::string, std::string>& nodes);
83     static bool SetupVirtualStylus();
84     static bool IsVirtualStylusOn();
85     bool SendEvent(const Context& ctx, struct input_event* event);
86     bool SendEvents(const Context& ctx, struct input_event* events, size_t nevents);
87 
88 private:
89     static VirtualPen virtualPen_;
90     static std::string devNode_;
91 };
92 
93 VirtualPen TransformPointTest::virtualPen_;
94 std::string TransformPointTest::devNode_;
95 
SetUpTestCase(void)96 void TransformPointTest::SetUpTestCase(void)
97 {
98     SetupVirtualStylus();
99 }
100 
TearDownTestCase(void)101 void TransformPointTest::TearDownTestCase(void)
102 {
103     if (!devNode_.empty()) {
104         virtualPen_.Close();
105         devNode_.clear();
106     }
107 }
108 
GetDeviceNodeName()109 std::string TransformPointTest::GetDeviceNodeName()
110 {
111     return devNode_;
112 }
113 
SendEvent(const Context & ctx,struct input_event * event)114 bool TransformPointTest::SendEvent(const Context& ctx, struct input_event* event)
115 {
116     CALL_INFO_TRACE;
117     MMI_HILOGD("Send input event.");
118     struct timeval tv;
119     if (gettimeofday(&tv, nullptr)) {
120         MMI_HILOGE("Failed to get current time.");
121         return false;
122     }
123     event->input_event_sec = tv.tv_sec;
124     event->input_event_usec = tv.tv_usec;
125 
126     const int fd = ctx.GetFd();
127     ssize_t ret = write(fd, event, sizeof(*event));
128     return (ret > 0);
129 }
130 
SendEvents(const Context & ctx,struct input_event * events,size_t nevents)131 bool TransformPointTest::SendEvents(const Context& ctx, struct input_event* events, size_t nevents)
132 {
133     CALL_INFO_TRACE;
134     if (!ctx.IsReady()) {
135         MMI_HILOGE("Device is not ready.");
136         return false;
137     }
138     MMI_HILOGD("%{public}zu input events to send.", nevents);
139     struct input_event* sp = events;
140     struct input_event* tp = sp + nevents;
141     for (; sp < tp; ++sp) {
142         if (!SendEvent(ctx, sp)) {
143             MMI_HILOGE("Failed to send event.");
144             break;
145         }
146         std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_FOR_EVENTS));
147     }
148     return (sp >= tp);
149 }
150 
Execute(const std::string & command,std::vector<std::string> & results)151 int TransformPointTest::Execute(const std::string& command, std::vector<std::string>& results)
152 {
153     CALL_INFO_TRACE;
154     MMI_HILOGD("Execute command:%{public}s.", command.c_str());
155     char buffer[DEFAULT_BUF_SIZE] {};
156     FILE* pin = popen(command.c_str(), "r");
157     if (!pin) {
158         MMI_HILOGE("Failed to popen command.");
159         return -1;
160     }
161     while (!feof(pin)) {
162         if (fgets(buffer, sizeof(buffer), pin) != nullptr) {
163             results.push_back(buffer);
164         }
165     }
166     MMI_HILOGD("Close phandle.");
167     return pclose(pin);
168 }
169 
GetInputDeviceNodes(std::map<std::string,std::string> & nodes)170 void TransformPointTest::GetInputDeviceNodes(std::map<std::string, std::string>& nodes)
171 {
172     CALL_INFO_TRACE;
173     std::string command { "cat /proc/bus/input/devices" };
174     std::vector<std::string> results;
175     Execute(command, results);
176     if (results.empty()) {
177         MMI_HILOGE("Failed to list devices.");
178         return;
179     }
180     const std::string kname { "Name=\"" };
181     const std::string kevent { "event" };
182     std::string name;
183     for (const auto &item : results) {
184         MMI_HILOGD("item:%{public}s.", item.c_str());
185         if (item[0] == 'N') {
186             std::string::size_type spos = item.find(kname);
187             if (spos != std::string::npos) {
188                 spos += kname.size();
189                 std::string::size_type tpos = item.find("\"", spos);
190                 if (tpos != std::string::npos) {
191                     name = item.substr(spos, tpos - spos);
192                 }
193             }
194         } else if (!name.empty() && (item[0] == 'H')) {
195             std::string::size_type spos = item.find(kevent);
196             if (spos != std::string::npos) {
197                 std::map<std::string, std::string>::const_iterator cItr = nodes.find(name);
198                 if (cItr != nodes.end()) {
199                     nodes.erase(cItr);
200                 }
201                 std::string::size_type tpos = spos + kevent.size();
202                 while (std::isalnum(item[tpos])) {
203                     ++tpos;
204                 }
205                 nodes.emplace(name, item.substr(spos, tpos - spos));
206                 name.clear();
207             }
208         }
209     }
210 }
211 
SetupVirtualStylus()212 bool TransformPointTest::SetupVirtualStylus()
213 {
214     CALL_INFO_TRACE;
215     MMI_HILOGD("Setup virtual stylus.");
216     if (!virtualPen_.SetUp()) {
217         MMI_HILOGE("Failed to setup virtual stylus.");
218         return false;
219     }
220     std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_FOR_INPUT));
221 
222     std::map<std::string, std::string> nodes;
223     GetInputDeviceNodes(nodes);
224     MMI_HILOGD("There are %{public}zu device nodes.", nodes.size());
225 
226     const std::string dev { "V-Pencil" };
227     std::map<std::string, std::string>::const_iterator cItr = nodes.find(dev);
228     if (cItr == nodes.cend()) {
229         MMI_HILOGE("No virtual stylus is found.");
230         return false;
231     }
232     MMI_HILOGD("Node name : \'%{public}s\'.", cItr->second.c_str());
233     std::ostringstream ss;
234     ss << "/dev/input/" << cItr->second;
235     devNode_ = ss.str();
236     return true;
237 }
238 
IsVirtualStylusOn()239 bool TransformPointTest::IsVirtualStylusOn()
240 {
241     return !devNode_.empty();
242 }
243 
244 static struct input_event inputEvents1[] {
245     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               7950   },
246     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_Y,               6400   },
247     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_X,          10     },
248     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_Y,          -10    },
249     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        30     },
250     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
251     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               8000   },
252     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        30     },
253     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
254     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               8050   },
255     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_Y,               6450   },
256     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        35     },
257     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
258     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               8100   },
259     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_Y,               6500   },
260     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        1510   },
261     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
262     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               8150   },
263     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        1520   },
264     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
265     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               8200   },
266     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_Y,               6550   },
267     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        1530   },
268     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
269     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               8200   },
270     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_Y,               6550   },
271     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        0      },
272     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
273 };
274 
275 /**
276  * @tc.name:MultimodalEventHandler_InjectKeyEvent_001
277  * @tc.desc:Verify inject key Back
278  * @tc.type: FUNC
279  * @tc.require:
280  */
281 HWTEST_F(TransformPointTest, TabletTransformPointProcesser1, TestSize.Level1)
282 {
283     CALL_INFO_TRACE;
284     ASSERT_TRUE(IsVirtualStylusOn());
285     Context ctx { GetDeviceNodeName() };
286     ASSERT_TRUE(SendEvents(ctx, inputEvents1, ARRAY_LENGTH(inputEvents1)));
287 }
288 
289 static struct input_event inputEvents2[] {
290     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               10752  },
291     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_Y,               22176  },
292     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_X,          90     },
293     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_Y,          90     },
294     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        0      },
295     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
296     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               10753  },
297     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_X,          -90    },
298     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
299     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_Y,               22177  },
300     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_Y,          -90    },
301     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        50     },
302     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
303     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_X,          90     },
304     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        1510   },
305     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
306     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               40000  },
307     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_X,          180    },
308     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        4096   },
309     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
310     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               50000  },
311     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_X,          270    },
312     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        100000 },
313     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
314     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_X,               60000  },
315     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_Y,               6550   },
316     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_TILT_X,          360    },
317     { .input_event_sec = 0, .input_event_usec = 0, EV_ABS, ABS_PRESSURE,        0      },
318     { .input_event_sec = 0, .input_event_usec = 0, EV_SYN, SYN_REPORT,          0      },
319 };
320 
321 HWTEST_F(TransformPointTest, TabletTransformPointProcesser2, TestSize.Level1)
322 {
323     CALL_INFO_TRACE;
324     ASSERT_TRUE(IsVirtualStylusOn());
325     Context ctx { GetDeviceNodeName() };
326     ASSERT_TRUE(SendEvents(ctx, inputEvents2, ARRAY_LENGTH(inputEvents2)));
327 }
328 } // namespace MMI
329 } // namespace OHOS
330