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