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