• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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