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