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