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