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 "random_test_flow.h"
17
18 #include <string>
19
20 #include "input_factory.h"
21 #include "report.h"
22 #include "wukong_define.h"
23 #include "ability_manager_client.h"
24 #include "component_manager.h"
25 #include "accessibility_ui_test_ability.h"
26
27 namespace OHOS {
28 namespace WuKong {
29 namespace {
30 const std::string RANDOM_TEST_HELP_MSG =
31 "usage: wukong exec [<arguments>]\n"
32 "These are wukong exec arguments list:\n"
33 " -h, --help random test help\n"
34 " -a, --appswitch appswitch event percent\n"
35 " -b, --bundle the bundle name of allowlist\n"
36 " -p, --prohibit the bundle name of blocklist\n"
37 " -d, --page block page list\n"
38 " -t, --touch touch event percent\n"
39 " -c, --count test count\n"
40 " -i, --interval interval\n"
41 " -s, --seed random seed\n"
42 " -m, --mouse mouse event percent\n"
43 " -k, --keyboard keyboard event percent\n"
44 " -H, --hardkey hardkey event percent\n"
45 " -S, --swap swap event percent\n"
46 " -T, --time test time\n"
47 " -C, --component component event percent\n"
48 " -r, --rotate rotate event percent\n";
49
50 const std::string SHORT_OPTIONS = "a:b:c:d:hi:k:p:s:t:T:H:m:S:C:r:";
51 const struct option LONG_OPTIONS[] = {
52 {"help", no_argument, nullptr, 'h'}, // help
53 {"seed", required_argument, nullptr, 's'}, // test seed
54 {"time", required_argument, nullptr, 'T'}, // test time
55 {"count", required_argument, nullptr, 'c'}, // test count
56 {"interval", required_argument, nullptr, 'i'}, // test interval
57 {"bundle", required_argument, nullptr, 'b'}, // test haps
58 {"appswitch", required_argument, nullptr, 'a'}, // switch app percent
59 {"keyboard", required_argument, nullptr, 'k'}, // keyboard percent
60 {"mouse", required_argument, nullptr, 'm'}, // mouse percent
61 {"touch", required_argument, nullptr, 't'}, // touch percent
62 {"swap", required_argument, nullptr, 'S'}, // swap percent
63 {"hardkey", required_argument, nullptr, 'H'}, // hardkey percent
64 {"prohibit", required_argument, nullptr, 'p'}, // prohibit
65 {"component", required_argument, nullptr, 'C'}, // prohibit
66 {"rotate", required_argument, nullptr, 'r'}, // rotate percent
67 {"page", required_argument, nullptr, 'd'}, // block page
68 };
69
70 /**
71 * WuKong default input action percent.
72 */
73 const vector<int> DEFAULT_INPUT_PERCENT = {
74 10, // INPUTTYPE_TOUCHINPUT, input touch event
75 3, // INPUTTYPE_SWAPINPUT, input swap event
76 1, // INPUTTYPE_MOUSEINPUT, input mouse event
77 2, // INPUTTYPE_KEYBOARDINPUT, input keyboard event
78 70, // INPUTTYPE_ELEMENTINPUT, input element event
79 10, // INPUTTYPE_APPSWITCHINPUT, input appswitch event
80 2, // INPUTTYPE_HARDKEYINPUT, input hardkey event
81 2 // INPUTTYPE_ROTATE, input rotate event
82 };
83
84 const map<int, InputType> OPTION_INPUT_PERCENT = {
85 {'a', INPUTTYPE_APPSWITCHINPUT}, // input appswitch event
86 {'C', INPUTTYPE_ELEMENTINPUT}, // input element event
87 {'k', INPUTTYPE_KEYBOARDINPUT}, // input keyboard event
88 {'S', INPUTTYPE_SWAPINPUT}, // input swap event
89 {'m', INPUTTYPE_MOUSEINPUT}, // input mouse event
90 {'t', INPUTTYPE_TOUCHINPUT}, // input touch event
91 {'H', INPUTTYPE_HARDKEYINPUT}, // input hardkey event
92 {'r', INPUTTYPE_ROTATEINPUT} // input rotate event
93 };
94
95 /**
96 * @brief Wukong block page
97 */
98 std::vector<std::string> systemPaths;
99
100 const int ONE_HUNDRED_PERCENT = 100;
101 // one minute (ms)
102 const int ONE_MINUTE = 60000;
103 // rotate
104 const int ROTATE = 114;
105 bool g_commandSEEDENABLE = false;
106 bool g_commandHELPENABLE = false;
107 bool g_commandTIMEENABLE = false;
108 bool g_commandCOUNTENABLE = false;
109 bool g_isAppStarted = false;
110 } // namespace
111 using namespace std;
112
RandomTestFlow(WuKongShellCommand & shellcommand)113 RandomTestFlow::RandomTestFlow(WuKongShellCommand &shellcommand)
114 : TestFlow(shellcommand),
115 inputPercent_(INPUTTYPE_INVALIDINPUT, 0)
116 {
117 }
118
~RandomTestFlow()119 RandomTestFlow::~RandomTestFlow()
120 {
121 if (timer_ != nullptr) {
122 timer_->Shutdown();
123 timer_->Unregister(timerId_);
124 timer_ = nullptr;
125 }
126 }
127
InitEventPercent()128 ErrCode RandomTestFlow::InitEventPercent()
129 {
130 int sumPercent = 0;
131 int sumLastDefaultPercent = ONE_HUNDRED_PERCENT;
132 vector<int> lastDefaultPercent = DEFAULT_INPUT_PERCENT;
133 for (auto input : inputPercent_) {
134 TRACK_LOG_STR("input: (%02d)", input);
135 }
136 for (int type = 0; type < INPUTTYPE_INVALIDINPUT; type++) {
137 // add type to count input list for random algorithm.
138 for (int index = 0; index < inputPercent_[type]; index++) {
139 eventList_.push_back(type);
140 }
141 // check argument percent, and set last default percent.
142 if (inputPercent_[type] > 0) {
143 sumLastDefaultPercent -= lastDefaultPercent[type];
144 lastDefaultPercent[type] = 0;
145 }
146 sumPercent += inputPercent_[type];
147 }
148 TRACK_LOG_STR("sumPercent: %d", sumPercent);
149 // check the sum percent more than 100%, and exit wukong.
150 if (sumPercent > ONE_HUNDRED_PERCENT) {
151 shellcommand_.ResultReceiverAppend("all event percentage more than 1, please reset params.\n");
152 shellcommand_.ResultReceiverAppend(RANDOM_TEST_HELP_MSG);
153 return OHOS::ERR_INVALID_VALUE;
154 }
155
156 // sum the last default percent for calculate last percent.
157 int lastPercent = ONE_HUNDRED_PERCENT - sumPercent;
158 int lastInputPercent = 0;
159 for (int type = 0; type < INPUTTYPE_INVALIDINPUT; type++) {
160 if (lastDefaultPercent[type] <= 0 || lastDefaultPercent[type] > ONE_HUNDRED_PERCENT ||
161 sumLastDefaultPercent <= 0) {
162 continue;
163 }
164 lastInputPercent = (int)(lastPercent * ((float)(lastDefaultPercent[type]) / sumLastDefaultPercent));
165 // add type to count input list for random algorithm.
166 for (int index = 0; index < lastInputPercent; index++) {
167 eventList_.push_back(type);
168 }
169 sumPercent += lastInputPercent;
170 }
171
172 // if the sumPercent less than 100%, add INPUTTYPE_TOUCHINPUT to random algorithm.
173 for (int index = 0; index < ONE_HUNDRED_PERCENT - sumPercent; index++) {
174 eventList_.push_back(INPUTTYPE_TOUCHINPUT);
175 }
176
177 return OHOS::ERR_OK;
178 }
179
EnvInit()180 ErrCode RandomTestFlow::EnvInit()
181 {
182 // init event list percent.
183 ErrCode result = InitEventPercent();
184 if (result != OHOS::ERR_OK) {
185 return result;
186 }
187
188 // init srand and print seed information.
189 if (g_commandSEEDENABLE) {
190 srand(seedArgs_);
191 } else {
192 time_t tempSeed = time(nullptr);
193 srand((unsigned int)tempSeed);
194 seedArgs_ = (int)time(nullptr);
195 }
196 Report::GetInstance()->SetSeed(std::to_string(seedArgs_));
197 TEST_RUN_LOG(("Seed: " + std::to_string(seedArgs_)).c_str());
198
199 // shuffle the event list.
200 RandomShuffle();
201
202 // if time test flow, register timer.
203 if (g_commandTIMEENABLE) {
204 RegisterTimer();
205 }
206 return result;
207 }
208
SetInputPercent(const int option)209 ErrCode RandomTestFlow::SetInputPercent(const int option)
210 {
211 InputType inputType = INPUTTYPE_INVALIDINPUT;
212 auto it = OPTION_INPUT_PERCENT.find(option);
213 if (it == OPTION_INPUT_PERCENT.end()) {
214 return OHOS::ERR_INVALID_VALUE;
215 }
216
217 inputType = it->second;
218 float percent = 0.0;
219 try {
220 percent = std::stof(optarg);
221 if ((it->first) == ROTATE && percent == 1) {
222 g_isAppStarted = true;
223 }
224 } catch (const std::exception &e) {
225 // try the option argument string convert float.
226 shellcommand_.ResultReceiverAppend("error: option '");
227 shellcommand_.ResultReceiverAppend(string((char *)(&option)));
228 shellcommand_.ResultReceiverAppend("' requires a value.\n");
229 shellcommand_.ResultReceiverAppend(RANDOM_TEST_HELP_MSG);
230 return OHOS::ERR_INVALID_VALUE;
231 }
232 // check valid of the option argument
233 if (percent > 1 || percent < 0) {
234 shellcommand_.ResultReceiverAppend("the input percent more than 1 (100%).\n");
235 shellcommand_.ResultReceiverAppend(RANDOM_TEST_HELP_MSG);
236 return OHOS::ERR_INVALID_VALUE;
237 }
238
239 // convert float to int (0 ~ 100)
240 inputPercent_[inputType] = (int)(percent * ONE_HUNDRED_PERCENT);
241 return OHOS::ERR_OK;
242 }
243
InputScene(std::shared_ptr<InputAction> inputaction,bool inputFlag)244 ErrCode RandomTestFlow::InputScene(std::shared_ptr<InputAction> inputaction, bool inputFlag)
245 {
246 ErrCode result = OHOS::ERR_OK;
247 if (inputFlag) {
248 result = inputaction->RandomInput();
249 } else {
250 ComponentManager::GetInstance()->BackToPrePage();
251 }
252 return result;
253 }
254
SetBlockPage(std::vector<std::string> systemPaths)255 bool RandomTestFlow::SetBlockPage(std::vector<std::string> systemPaths)
256 {
257 auto root = std::make_shared<OHOS::Accessibility::AccessibilityElementInfo>();
258 auto accPtr = OHOS::Accessibility::AccessibilityUITestAbility::GetInstance();
259 // Get root AccessibilityElementInfo from Accessibility
260 accPtr->GetRoot(*(root.get()));
261 std::string path = root->GetPagePath();
262 bool inputFlag = true;
263 TRACK_LOG_STR("Componentpage path: (%s)", path.c_str());
264 for (string systemPath : systemPaths) {
265 if (path.find(systemPath) != string::npos) {
266 INFO_LOG_STR("Block the current page and return. Block page : (%s)", path.c_str());
267 inputFlag = false;
268 break;
269 }
270 }
271 return inputFlag;
272 }
273
RunStep()274 ErrCode RandomTestFlow::RunStep()
275 {
276 ErrCode result;
277 // control the count test flow
278 if (g_commandCOUNTENABLE == true) {
279 totalCount_--;
280 if (totalCount_ < 0) {
281 isFinished_ = true;
282 return OHOS::ERR_OK;
283 }
284 }
285 bool inputFlag = SetBlockPage(systemPaths);
286 std::shared_ptr<InputAction> inputaction = nullptr;
287 if (!g_isAppStarted) {
288 inputaction = InputFactory::GetInputAction(INPUTTYPE_APPSWITCHINPUT);
289 if (inputaction == nullptr) {
290 ERROR_LOG("inputaction is nullptr");
291 return OHOS::ERR_INVALID_VALUE;
292 }
293 result = InputScene(inputaction, inputFlag);
294 if (result != OHOS::ERR_OK) {
295 ERROR_LOG("launch app failed and exit");
296 return result;
297 }
298 inputaction = nullptr;
299 g_isAppStarted = true;
300 usleep(intervalArgs_ * oneSecond_);
301 }
302 // input event, get event index form event list by random algorithm.
303 int eventindex = rand() % ONE_HUNDRED_PERCENT;
304 InputType eventTypeId = (InputType)(eventList_.at(eventindex));
305 inputaction = InputFactory::GetInputAction(eventTypeId);
306 if (inputaction == nullptr) {
307 ERROR_LOG("inputaction is nullptr");
308 return OHOS::ERR_INVALID_VALUE;
309 }
310
311 if (ProtectRightAbility(inputaction, eventTypeId) == OHOS::ERR_INVALID_VALUE) {
312 return OHOS::ERR_INVALID_VALUE;
313 }
314 result = InputScene(inputaction, inputFlag);
315 usleep(intervalArgs_ * oneSecond_);
316 return result;
317 }
318
ProtectRightAbility(std::shared_ptr<InputAction> & inputaction,InputType & eventTypeId)319 ErrCode RandomTestFlow::ProtectRightAbility(std::shared_ptr<InputAction> &inputaction, InputType &eventTypeId)
320 {
321 std::vector<std::string> allowList;
322 WuKongUtil::GetInstance()->GetAllowList(allowList);
323 if (allowList.size() > 0) {
324 std::string bundleName = "com.ohos.launcher";
325 auto elementName = AAFwk::AbilityManagerClient::GetInstance()->GetTopAbility();
326 if (elementName.GetBundleName() == bundleName) {
327 if (eventTypeId == INPUTTYPE_TOUCHINPUT || eventTypeId == INPUTTYPE_ELEMENTINPUT) {
328 return OHOS::ERR_INVALID_VALUE;
329 }
330 }
331 // allowList 数量大于0 并且 elementName.GetBundleName() 不在allowList里面,重新拉起一个应用
332 auto curBundleName = elementName.GetBundleName();
333 auto it = find(allowList.begin(), allowList.end(), curBundleName);
334 if (it == allowList.end()) {
335 inputaction = InputFactory::GetInputAction(INPUTTYPE_APPSWITCHINPUT);
336 if (inputaction == nullptr) {
337 ERROR_LOG("inputaction is nullptr");
338 return OHOS::ERR_INVALID_VALUE;
339 }
340 }
341 }
342 return OHOS::ERR_OK;
343 }
344
HandleNormalOption(const int option)345 ErrCode RandomTestFlow::HandleNormalOption(const int option)
346 {
347 ErrCode result = OHOS::ERR_OK;
348 switch (option) {
349 case 't':
350 case 'm':
351 case 'S':
352 case 'k':
353 case 'H':
354 case 'a':
355 case 'r':
356 case 'C': {
357 result = SetInputPercent(option);
358 break;
359 }
360 case 'b': {
361 result = WuKongUtil::GetInstance()->SetAllowList(optarg);
362 break;
363 }
364 case 'c': {
365 // check if the '-c' and 'T' is exist at the same time
366 result = CheckArgument(option);
367 break;
368 }
369 case 'h': {
370 shellcommand_.ResultReceiverAppend(RANDOM_TEST_HELP_MSG);
371 result = OHOS::ERR_NO_INIT;
372 g_commandHELPENABLE = true;
373 break;
374 }
375 case 'i': {
376 intervalArgs_ = std::stoi(optarg);
377 TEST_RUN_LOG(("Interval: " + std::to_string(intervalArgs_)).c_str());
378 break;
379 }
380 case 's': {
381 seedArgs_ = std::stoi(optarg);
382 g_commandSEEDENABLE = true;
383 break;
384 }
385 case 'T': {
386 // check if the '-c' and 'T' is exist at the same time
387 result = CheckArgument(option);
388 break;
389 }
390 case 'p': {
391 result = WuKongUtil::GetInstance()->SetBlockList(optarg);
392 break;
393 }
394 case 'd': {
395 result = WuKongUtil::GetInstance()->SetBlockPageList(optarg);
396 break;
397 }
398 default: {
399 result = OHOS::ERR_INVALID_VALUE;
400 break;
401 }
402 }
403 WuKongUtil::GetInstance()->GetBlockPageList(systemPaths);
404 WuKongUtil::GetInstance()->SetOrderFlag(false);
405 return result;
406 }
407
CheckArgument(const int option)408 ErrCode RandomTestFlow::CheckArgument(const int option)
409 {
410 ErrCode result = OHOS::ERR_OK;
411 switch (option) {
412 case 'c': {
413 // check if the '-c' and 'T' is exist at the same time
414 if (g_commandTIMEENABLE == false) {
415 g_commandCOUNTENABLE = true;
416 countArgs_ = std::stoi(optarg);
417 TEST_RUN_LOG(("Count: " + std::to_string(countArgs_)).c_str());
418 totalCount_ = countArgs_;
419 } else {
420 DEBUG_LOG(PARAM_COUNT_TIME_ERROR);
421 shellcommand_.ResultReceiverAppend(std::string(PARAM_COUNT_TIME_ERROR) + "\n");
422 result = OHOS::ERR_INVALID_VALUE;
423 }
424 break;
425 }
426 case 'T': {
427 // check if the '-c' and 'T' is exist at the same time
428 if (g_commandCOUNTENABLE == false) {
429 totalTime_ = std::stof(optarg);
430 TEST_RUN_LOG(("Time: " + std::to_string(totalTime_)).c_str());
431 g_commandTIMEENABLE = true;
432 } else {
433 DEBUG_LOG(PARAM_TIME_COUNT_ERROR);
434 shellcommand_.ResultReceiverAppend(std::string(PARAM_TIME_COUNT_ERROR) + "\n");
435 result = OHOS::ERR_INVALID_VALUE;
436 }
437 break;
438 }
439 default: {
440 result = OHOS::ERR_INVALID_VALUE;
441 break;
442 }
443 }
444 return result;
445 }
446
GetOptionArguments(std::string & shortOpts)447 const struct option *RandomTestFlow::GetOptionArguments(std::string &shortOpts)
448 {
449 shortOpts = SHORT_OPTIONS;
450 return LONG_OPTIONS;
451 }
452
HandleUnknownOption(const char optopt)453 ErrCode RandomTestFlow::HandleUnknownOption(const char optopt)
454 {
455 ErrCode result = OHOS::ERR_OK;
456 switch (optopt) {
457 case 'a':
458 case 'b':
459 case 'c':
460 case 'd':
461 case 'i':
462 case 's':
463 case 't':
464 case 'r':
465 case 'S':
466 case 'p':
467 case 'k':
468 case 'H':
469 case 'T':
470 case 'm':
471 case 'C':
472 // error: option 'x' requires a value.
473 shellcommand_.ResultReceiverAppend("error: option '-");
474 shellcommand_.ResultReceiverAppend(string(1, optopt));
475 shellcommand_.ResultReceiverAppend("' requires a value.\n");
476 result = OHOS::ERR_INVALID_VALUE;
477 break;
478 case 'h': {
479 result = OHOS::ERR_INVALID_VALUE;
480 break;
481 }
482 default: {
483 // 'wukong exec' with an unknown option: wukong exec -x
484 shellcommand_.ResultReceiverAppend(
485 "'wukong exec' with an unknown option, please reference help information:\n");
486 result = OHOS::ERR_INVALID_VALUE;
487 break;
488 }
489 }
490 shellcommand_.ResultReceiverAppend(RANDOM_TEST_HELP_MSG);
491 return result;
492 }
493
RandomShuffle()494 void RandomTestFlow::RandomShuffle()
495 {
496 for (uint32_t i = eventList_.size() - 1; i > 0; --i) {
497 std::swap(eventList_[i], eventList_[std::rand() % (i + 1)]);
498 }
499 }
500
RegisterTimer()501 void RandomTestFlow::RegisterTimer()
502 {
503 if (timer_ == nullptr) {
504 timer_ = std::make_shared<Utils::Timer>("wukong");
505 timerId_ = timer_->Register(std::bind(&RandomTestFlow::TestTimeout, this), totalTime_ * ONE_MINUTE, true);
506 timer_->Setup();
507 }
508 }
509
TestTimeout()510 void RandomTestFlow::TestTimeout()
511 {
512 g_commandTIMEENABLE = false;
513 isFinished_ = true;
514 }
515 } // namespace WuKong
516 } // namespace OHOS
517