1 /*
2 * Copyright (c) 2025 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 <gtest/gtest.h>
17 #include <fstream>
18 #include <string>
19 #include <map>
20
21 #include "event_replayer.h"
22
23 namespace OHOS {
24 namespace MMI {
25 namespace {
26 using namespace testing::ext;
27
28 const std::string SANDBOX_PATH = "/data/service/el1/public/multimodalinput/";
29 const std::string TEST_FILE_PATH = SANDBOX_PATH + "mmi_test_events.rec";
30 const std::string INVALID_FILE_PATH = SANDBOX_PATH + "mmi_invalid_events.rec";
31 const std::string EMPTY_FILE_PATH = SANDBOX_PATH + "mmi_empty_events.rec";
32 const std::string PARTIAL_FILE_PATH = SANDBOX_PATH + "mmi_partial_events.rec";
33
CreateTestEventFile(const std::string & path)34 bool CreateTestEventFile(const std::string& path)
35 {
36 std::ofstream file(path);
37 if (!file.is_open()) {
38 return false;
39 }
40 file << "EVENTS_BEGIN" << std::endl;
41 file << "[1, 1, 30, 1, 1682345678, 123456] # EV_KEY / KEY_A 1" << std::endl;
42 file << "[1, 0, 0, 0, 1682345678, 123456] # EV_SYN / SYN_REPORT 0" << std::endl;
43 file << "[1, 1, 30, 0, 1682345678, 223456] # EV_KEY / KEY_A 0" << std::endl;
44 file << "[1, 0, 0, 0, 1682345678, 223456] # EV_SYN / SYN_REPORT 0" << std::endl;
45 file << "EVENTS_END" << std::endl;
46 file << std::endl;
47 file << "DEVICES: 1" << std::endl;
48 file << "DEVICE: 1|/dev/input/event2|Test Keyboard" << std::endl;
49 file.close();
50 return true;
51 }
52
CreateTestDeviceFile(const std::string & path)53 bool CreateTestDeviceFile(const std::string& path)
54 {
55 std::ofstream file(path);
56 if (!file.is_open()) {
57 return false;
58 }
59 file << "EVENTS_BEGIN" << std::endl;
60 file << "[1, 1, 30, 1, 1682345678, 123456] # EV_KEY / KEY_A 1" << std::endl;
61 file << "[1, 0, 0, 0, 1682345678, 123456] # EV_SYN / SYN_REPORT 0" << std::endl;
62 file << "[1, 1, 30, 0, 1682345678, 223456] # EV_KEY / KEY_A 0" << std::endl;
63 file << "[1, 0, 0, 0, 1682345678, 223456] # EV_SYN / SYN_REPORT 0" << std::endl;
64 file << "EVENTS_END" << std::endl;
65 file << std::endl;
66 file << "DEVICES: 1" << std::endl;
67 file << "DEVICE: 1|/dev/input/event2|Test Keyboard|123124a" << std::endl;
68 file.close();
69 return true;
70 }
71
CreateInvalidEventFile(const std::string & path)72 bool CreateInvalidEventFile(const std::string& path)
73 {
74 std::ofstream file(path);
75 if (!file.is_open()) {
76 return false;
77 }
78 file << "INVALID CONTENT" << std::endl;
79 file.close();
80 return true;
81 }
82
CreateEmptyEventFile(const std::string & path)83 bool CreateEmptyEventFile(const std::string& path)
84 {
85 std::ofstream file(path);
86 if (!file.is_open()) {
87 return false;
88 }
89 file.close();
90 return true;
91 }
92
CreatePartialEventFile(const std::string & path,bool includeDevices,bool includeEvents)93 bool CreatePartialEventFile(const std::string& path, bool includeDevices, bool includeEvents)
94 {
95 std::ofstream file(path);
96 if (!file.is_open()) {
97 return false;
98 }
99 file << "HEADER INFORMATION" << std::endl;
100 if (includeEvents) {
101 file << "EVENTS_BEGIN" << std::endl;
102 file << "[4, 2, 0, -2, 1501837700, 841124] # EV_REL / REL_X -2" << std::endl;
103 file << "[4, 0, 0, 0, 1501837700, 841124] # EV_SYN / SYN_REPORT 0" << std::endl;
104 file << "EVENTS_END" << std::endl;
105 }
106 if (includeDevices) {
107 file << "DEVICES: 2" << std::endl;
108 file << "DEVICE: 4|/dev/input/event4|Compx 2.4G Receiver Mouse" << std::endl;
109 file << "DEVICE: 10|/dev/input/event10|VSoC touchscreen" << std::endl;
110 }
111 file.close();
112 return true;
113 }
114
CleanupTestFiles()115 void CleanupTestFiles()
116 {
117 std::remove(TEST_FILE_PATH.c_str());
118 std::remove(INVALID_FILE_PATH.c_str());
119 std::remove(EMPTY_FILE_PATH.c_str());
120 std::remove(PARTIAL_FILE_PATH.c_str());
121 }
122 } // namespace
123
124 class EventReplayerTest : public testing::Test {
125 public:
SetUpTestCase(void)126 static void SetUpTestCase(void) {}
TearDownTestCase(void)127 static void TearDownTestCase(void)
128 {
129 CleanupTestFiles();
130 }
SetUp()131 void SetUp() {}
TearDown()132 void TearDown()
133 {
134 CleanupTestFiles();
135 }
136 };
137
138 /**
139 * @tc.name: EventReplayerTest_Constructor
140 * @tc.desc: Test constructor of EventReplayer
141 * @tc.type: FUNC
142 * @tc.require:
143 */
144 HWTEST_F(EventReplayerTest, EventReplayerTest_Constructor, TestSize.Level1)
145 {
146 EventReplayer replayer1(TEST_FILE_PATH);
147 SUCCEED();
148
149 std::map<uint16_t, uint16_t> deviceMapping = {{1, 3}, {2, 4}};
150 EventReplayer replayer2(TEST_FILE_PATH, deviceMapping);
151 SUCCEED();
152 }
153
154 /**
155 * @tc.name: EventReplayerTest_ParseInputLine_Valid
156 * @tc.desc: Test parsing valid input lines
157 * @tc.type: FUNC
158 * @tc.require:
159 */
160 HWTEST_F(EventReplayerTest, EventReplayerTest_ParseInputLine_Valid, TestSize.Level1)
161 {
162 uint32_t deviceId;
163 input_event event;
164
165 EXPECT_TRUE(EventReplayer::ParseInputLine("[1, 1, 30, 1, 1682345678, 123456] # EV_KEY / KEY_A 1",
166 deviceId, event));
167 EXPECT_EQ(deviceId, 1);
168 EXPECT_EQ(event.type, 1);
169 EXPECT_EQ(event.code, 30);
170 EXPECT_EQ(event.value, 1);
171 EXPECT_EQ(event.input_event_sec, 1682345678);
172 EXPECT_EQ(event.input_event_usec, 123456);
173
174 EXPECT_TRUE(EventReplayer::ParseInputLine("[ 2, 3, 40, 0, 1682345679, 234567 ] # Comment",
175 deviceId, event));
176 EXPECT_EQ(deviceId, 2);
177 EXPECT_EQ(event.type, 3);
178 EXPECT_EQ(event.code, 40);
179 EXPECT_EQ(event.value, 0);
180 EXPECT_EQ(event.input_event_sec, 1682345679);
181 EXPECT_EQ(event.input_event_usec, 234567);
182
183 EXPECT_TRUE(EventReplayer::ParseInputLine("[3, 0, 0, 0, 1682345680, 345678]",
184 deviceId, event));
185 EXPECT_EQ(deviceId, 3);
186 EXPECT_EQ(event.type, 0);
187 EXPECT_EQ(event.code, 0);
188 EXPECT_EQ(event.value, 0);
189 EXPECT_EQ(event.input_event_sec, 1682345680);
190 EXPECT_EQ(event.input_event_usec, 345678);
191 }
192
193 /**
194 * @tc.name: EventReplayerTest_ParseInputLine_Invalid
195 * @tc.desc: Test parsing invalid input lines
196 * @tc.type: FUNC
197 * @tc.require:
198 */
199 HWTEST_F(EventReplayerTest, EventReplayerTest_ParseInputLine_Invalid, TestSize.Level1)
200 {
201 uint32_t deviceId;
202 input_event event;
203
204 EXPECT_FALSE(EventReplayer::ParseInputLine("", deviceId, event));
205 EXPECT_FALSE(EventReplayer::ParseInputLine("1, 1, 30, 1, 1682345678, 123456", deviceId, event));
206 EXPECT_FALSE(EventReplayer::ParseInputLine("[1, 1, 30]", deviceId, event));
207 EXPECT_FALSE(EventReplayer::ParseInputLine("[a, 1, 30, 1, 1682345678, 123456]", deviceId, event));
208 EXPECT_FALSE(EventReplayer::ParseInputLine("[ 1, 1, 30, 1, 1682345678, 123456", deviceId, event));
209 EXPECT_FALSE(EventReplayer::ParseInputLine("1, 1, 30, 1, 1682345678, 123456 ]", deviceId, event));
210 EXPECT_FALSE(EventReplayer::ParseInputLine("[1 1, 30, 1, 1682345678, 123456]", deviceId, event));
211 }
212
213 /**
214 * @tc.name: EventReplayerTest_FileOpenError
215 * @tc.desc: Test file opening error cases
216 * @tc.type: FUNC
217 * @tc.require:
218 */
219 HWTEST_F(EventReplayerTest, EventReplayerTest_FileOpenError, TestSize.Level1)
220 {
221 std::string nonExistentPath = SANDBOX_PATH + "non_existent_file.rec";
222 EventReplayer replayer(nonExistentPath);
223 EXPECT_FALSE(replayer.Replay());
224 }
225
226 /**
227 * @tc.name: EventReplayerTest_EmptyFileError
228 * @tc.desc: Test with empty file error cases
229 * @tc.type: FUNC
230 * @tc.require:
231 */
232 HWTEST_F(EventReplayerTest, EventReplayerTest_EmptyFileError, TestSize.Level1)
233 {
234 if (!CreateEmptyEventFile(EMPTY_FILE_PATH)) {
235 GTEST_SKIP() << "Failed to create test file, skipping test";
236 }
237 EventReplayer replayer(EMPTY_FILE_PATH);
238 EXPECT_FALSE(replayer.Replay());
239 }
240
241 /**
242 * @tc.name: EventReplayerTest_InvalidFileFormat
243 * @tc.desc: Test with invalid file format
244 * @tc.type: FUNC
245 * @tc.require:
246 */
247 HWTEST_F(EventReplayerTest, EventReplayerTest_InvalidFileFormat, TestSize.Level1)
248 {
249 if (!CreateInvalidEventFile(INVALID_FILE_PATH)) {
250 GTEST_SKIP() << "Failed to create test file, skipping test";
251 }
252 EventReplayer replayer(INVALID_FILE_PATH);
253 EXPECT_FALSE(replayer.Replay());
254 }
255
256 /**
257 * @tc.name: EventReplayerTest_InvaildFilePath
258 * @tc.desc: Test with invalid file path
259 * @tc.type: FUNC
260 * @tc.require:
261 */
262 HWTEST_F(EventReplayerTest, EventReplayerTest_InvaildFilePath, TestSize.Level1)
263 {
264 EventReplayer replayer("/invaild/path");
265 EXPECT_FALSE(replayer.Replay());
266 }
267
268 /**
269 * @tc.name: EventReplayerTest_VaildFilePath
270 * @tc.desc: Test with valid file path but no available device
271 * @tc.type: FUNC
272 * @tc.require:
273 */
274 HWTEST_F(EventReplayerTest, EventReplayerTest_VaildFilePath, TestSize.Level1)
275 {
276 if (!CreateTestDeviceFile(TEST_FILE_PATH)) {
277 GTEST_SKIP() << "Failed to create test file, skipping test";
278 }
279 EventReplayer replayer(TEST_FILE_PATH);
280 EXPECT_FALSE(replayer.Replay());
281 }
282
283 /**
284 * @tc.name: EventReplayerTest_DeviceMapping
285 * @tc.desc: Test device mapping functionality
286 * @tc.type: FUNC
287 * @tc.require:
288 */
289 HWTEST_F(EventReplayerTest, EventReplayerTest_DeviceMapping, TestSize.Level1)
290 {
291 std::map<uint16_t, uint16_t> deviceMapping = {{1, 3}, {2, 4}};
292 EventReplayer replayer(TEST_FILE_PATH, deviceMapping);
293 SUCCEED();
294 }
295
296 /**
297 * @tc.name: EventReplayerTest_InvalidDeviceError
298 * @tc.desc: Test error handling when device is invalid
299 * @tc.type: FUNC
300 * @tc.require:
301 */
302 HWTEST_F(EventReplayerTest, EventReplayerTest_InvalidDeviceError, TestSize.Level1)
303 {
304 if (!CreateTestEventFile(TEST_FILE_PATH)) {
305 GTEST_SKIP() << "Failed to create test file, skipping test";
306 }
307 std::map<uint16_t, uint16_t> deviceMapping = {{2, 3}, {3, 4}};
308 EventReplayer replayer(TEST_FILE_PATH, deviceMapping);
309 ASSERT_NO_FATAL_FAILURE({
310 replayer.Replay();
311 });
312 }
313
314 /**
315 * @tc.name: EventReplayerTest_SeekToDevicesSection_Found
316 * @tc.desc: Test seeking to DEVICES section when it exists
317 * @tc.type: FUNC
318 * @tc.require:
319 */
320 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToDevicesSection_Found, TestSize.Level1)
321 {
322 if (!CreatePartialEventFile(PARTIAL_FILE_PATH, true, false)) {
323 GTEST_SKIP() << "Failed to create test file, skipping test";
324 }
325 EventReplayer replayer(PARTIAL_FILE_PATH);
326 std::ifstream inputFile(PARTIAL_FILE_PATH);
327 EXPECT_TRUE(inputFile.is_open()) << "Failed to open test file";
328 EXPECT_TRUE(replayer.SeekToDevicesSection(inputFile));
329 // Verify we're at the right position
330 std::string line;
331 std::getline(inputFile, line);
332 EXPECT_EQ(line, "DEVICES: 2") << "File position should be at DEVICES line";
333
334 inputFile.close();
335 }
336
337 /**
338 * @tc.name: EventReplayerTest_SeekToDevicesSection_NotFound
339 * @tc.desc: Test seeking to DEVICES section when it doesn't exist
340 * @tc.type: FUNC
341 * @tc.require:
342 */
343 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToDevicesSection_NotFound, TestSize.Level1)
344 {
345 if (!CreatePartialEventFile(PARTIAL_FILE_PATH, false, true)) {
346 GTEST_SKIP() << "Failed to create test file, skipping test";
347 }
348 EventReplayer replayer(PARTIAL_FILE_PATH);
349 std::ifstream inputFile(PARTIAL_FILE_PATH);
350 EXPECT_TRUE(inputFile.is_open()) << "Failed to open test file";
351 EXPECT_FALSE(replayer.SeekToDevicesSection(inputFile));
352 inputFile.close();
353 }
354
355 /**
356 * @tc.name: EventReplayerTest_SeekToEventsSection_Found
357 * @tc.desc: Test seeking to EVENTS_BEGIN section when it exists
358 * @tc.type: FUNC
359 * @tc.require:
360 */
361 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToEventsSection_Found, TestSize.Level1)
362 {
363 if (!CreatePartialEventFile(PARTIAL_FILE_PATH, false, true)) {
364 GTEST_SKIP() << "Failed to create test file, skipping test";
365 }
366 EventReplayer replayer(PARTIAL_FILE_PATH);
367 std::ifstream inputFile(PARTIAL_FILE_PATH);
368 EXPECT_TRUE(inputFile.is_open()) << "Failed to open test file";
369 EXPECT_TRUE(replayer.SeekToEventsSection(inputFile));
370 // Verify we're at the right position (after EVENTS_BEGIN)
371 std::string line;
372 std::getline(inputFile, line);
373 EXPECT_EQ(line, "[4, 2, 0, -2, 1501837700, 841124] # EV_REL / REL_X -2") <<
374 "File position should be after EVENTS_BEGIN line";
375 inputFile.close();
376 }
377
378 /**
379 * @tc.name: EventReplayerTest_SeekToEventsSection_NotFound
380 * @tc.desc: Test seeking to EVENTS_BEGIN section when it doesn't exist
381 * @tc.type: FUNC
382 * @tc.require:
383 */
384 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToEventsSection_NotFound, TestSize.Level1)
385 {
386 if (!CreatePartialEventFile(PARTIAL_FILE_PATH, true, false)) {
387 GTEST_SKIP() << "Failed to create test file, skipping test";
388 }
389 EventReplayer replayer(PARTIAL_FILE_PATH);
390 std::ifstream inputFile(PARTIAL_FILE_PATH);
391 EXPECT_TRUE(inputFile.is_open()) << "Failed to open test file";
392 EXPECT_FALSE(replayer.SeekToEventsSection(inputFile));
393 inputFile.close();
394 }
395
396 /**
397 * @tc.name: EventReplayerTest_SeekToDevicesSection_BadStream
398 * @tc.desc: Test seeking to DEVICES section with a bad file stream
399 * @tc.type: FUNC
400 * @tc.require:
401 */
402 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToDevicesSection_BadStream, TestSize.Level1)
403 {
404 EventReplayer replayer(INVALID_FILE_PATH);
405 std::ifstream inputFile(INVALID_FILE_PATH);
406 inputFile.close(); // Deliberately close to make it bad
407 EXPECT_FALSE(replayer.SeekToDevicesSection(inputFile));
408 }
409
410 /**
411 * @tc.name: EventReplayerTest_SeekToEventsSection_BadStream
412 * @tc.desc: Test seeking to EVENTS_BEGIN section with a bad file stream
413 * @tc.type: FUNC
414 * @tc.require:
415 */
416 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToEventsSection_BadStream, TestSize.Level1)
417 {
418 EventReplayer replayer(INVALID_FILE_PATH);
419 std::ifstream inputFile(INVALID_FILE_PATH);
420 inputFile.close(); // Deliberately close to make it bad
421 EXPECT_FALSE(replayer.SeekToEventsSection(inputFile));
422 }
423
424 /**
425 * @tc.name: EventReplayerTest_SeekToDevicesSection_CompleteFile
426 * @tc.desc: Test seeking to DEVICES section in a complete file with both sections
427 * @tc.type: FUNC
428 * @tc.require:
429 */
430 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToDevicesSection_CompleteFile, TestSize.Level1)
431 {
432 if (!CreateTestEventFile(TEST_FILE_PATH)) {
433 GTEST_SKIP() << "Failed to create test file, skipping test";
434 }
435 EventReplayer replayer(TEST_FILE_PATH);
436 std::ifstream inputFile(TEST_FILE_PATH);
437 EXPECT_TRUE(inputFile.is_open()) << "Failed to open test file";
438 EXPECT_TRUE(replayer.SeekToDevicesSection(inputFile));
439 // Verify we're at the right position
440 std::string line;
441 std::getline(inputFile, line);
442 EXPECT_EQ(line, "DEVICES: 1") << "File position should be at DEVICES line";
443 inputFile.close();
444 }
445
446 /**
447 * @tc.name: EventReplayerTest_SeekToEventsSection_CompleteFile
448 * @tc.desc: Test seeking to EVENTS_BEGIN section in a complete file with both sections
449 * @tc.type: FUNC
450 * @tc.require:
451 */
452 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToEventsSection_CompleteFile, TestSize.Level1)
453 {
454 if (!CreateTestEventFile(TEST_FILE_PATH)) {
455 GTEST_SKIP() << "Failed to create test file, skipping test";
456 }
457 EventReplayer replayer(TEST_FILE_PATH);
458 std::ifstream inputFile(TEST_FILE_PATH);
459 EXPECT_TRUE(inputFile.is_open()) << "Failed to open test file";
460 EXPECT_TRUE(replayer.SeekToEventsSection(inputFile));
461 // Verify we're at the right position (after EVENTS_BEGIN)
462 std::string line;
463 std::getline(inputFile, line);
464 EXPECT_EQ(line, "[1, 1, 30, 1, 1682345678, 123456] # EV_KEY / KEY_A 1") <<
465 "File position should be after EVENTS_BEGIN line";
466 inputFile.close();
467 }
468
469 /**
470 * @tc.name: EventReplayerTest_SeekToDevicesSection_EmptyFile
471 * @tc.desc: Test seeking to DEVICES section in an empty file
472 * @tc.type: FUNC
473 * @tc.require:
474 */
475 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToDevicesSection_EmptyFile, TestSize.Level1)
476 {
477 if (!CreateEmptyEventFile(EMPTY_FILE_PATH)) {
478 GTEST_SKIP() << "Failed to create test file, skipping test";
479 }
480 EventReplayer replayer(EMPTY_FILE_PATH);
481 std::ifstream inputFile(EMPTY_FILE_PATH);
482 EXPECT_TRUE(inputFile.is_open()) << "Failed to open test file";
483 EXPECT_FALSE(replayer.SeekToDevicesSection(inputFile));
484 inputFile.close();
485 }
486
487 /**
488 * @tc.name: EventReplayerTest_SeekToEventsSection_EmptyFile
489 * @tc.desc: Test seeking to EVENTS_BEGIN section in an empty file
490 * @tc.type: FUNC
491 * @tc.require:
492 */
493 HWTEST_F(EventReplayerTest, EventReplayerTest_SeekToEventsSection_EmptyFile, TestSize.Level1)
494 {
495 if (!CreateEmptyEventFile(EMPTY_FILE_PATH)) {
496 GTEST_SKIP() << "Failed to create test file, skipping test";
497 }
498 EventReplayer replayer(EMPTY_FILE_PATH);
499 std::ifstream inputFile(EMPTY_FILE_PATH);
500 EXPECT_TRUE(inputFile.is_open()) << "Failed to open test file";
501 EXPECT_FALSE(replayer.SeekToEventsSection(inputFile));
502 inputFile.close();
503 }
504 } // namespace MMI
505 } // namespace OHOS