1#!/usr/bin/env python3 2# coding=utf-8 3 4# 5# Copyright (c) 2022 Huawei Device Co., Ltd. 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19import os 20import re 21import subprocess 22import zipfile 23import stat 24import time 25import json 26from dataclasses import dataclass 27from tempfile import TemporaryDirectory 28from tempfile import NamedTemporaryFile 29from multiprocessing import Process 30from multiprocessing import Queue 31 32from xdevice import ITestKit 33from xdevice import platform_logger 34from xdevice import Plugin 35from xdevice import ParamError 36from xdevice import get_file_absolute_path 37from xdevice import get_config_value 38from xdevice import exec_cmd 39from xdevice import ConfigConst 40from xdevice import AppInstallError 41from xdevice import convert_serial 42from xdevice import check_path_legal 43from xdevice import modify_props 44from xdevice import get_app_name_by_tool 45from xdevice import remount 46from xdevice import disable_keyguard 47from xdevice import get_class 48from xdevice import get_cst_time 49 50from ohos.constants import CKit 51from ohos.environment.dmlib import HdcHelper 52from ohos.environment.dmlib import CollectingOutputReceiver 53 54__all__ = ["STSKit", "CommandKit", "PushKit", "PropertyCheckKit", "ShellKit", "WifiKit", 55 "ConfigKit", "AppInstallKit", "ComponentKit", "PermissionKit", 56 "junit_dex_para_parse", "SmartPerfKit"] 57 58MAX_WAIT_COUNT = 4 59TARGET_SDK_VERSION = 22 60 61LOG = platform_logger("Kit") 62 63 64@Plugin(type=Plugin.TEST_KIT, id=CKit.command) 65class CommandKit(ITestKit): 66 67 def __init__(self): 68 self.run_command = [] 69 self.teardown_command = [] 70 self.paths = "" 71 72 def __check_config__(self, config): 73 self.paths = get_config_value('paths', config) 74 self.teardown_command = get_config_value('teardown', config) 75 self.run_command = get_config_value('shell', config) 76 77 def __setup__(self, device, **kwargs): 78 del kwargs 79 LOG.debug("CommandKit setup, device:{}, params:{}". 80 format(device, self.get_plugin_config().__dict__)) 81 if len(self.run_command) == 0: 82 LOG.info("No setup_command to run, skipping!") 83 return 84 for command in self.run_command: 85 self._run_command(command, device) 86 87 def __teardown__(self, device): 88 LOG.debug("CommandKit teardown: device:{}, params:{}".format 89 (device, self.get_plugin_config().__dict__)) 90 if len(self.teardown_command) == 0: 91 LOG.info("No teardown_command to run, skipping!") 92 return 93 for command in self.teardown_command: 94 self._run_command(command, device) 95 96 def _run_command(self, command, device): 97 98 command_type = command.get("name").strip() 99 command_value = command.get("value") 100 101 if command_type == "reboot": 102 device.reboot() 103 elif command_type == "install": 104 LOG.debug("Trying to install package {}".format(command_value)) 105 package = get_file_absolute_path(command_value, self.paths) 106 if not package or not os.path.exists(package): 107 LOG.error( 108 "The package {} to be installed does not exist".format( 109 package)) 110 111 result = device.install_package(package) 112 if not result.startswith("Success") and "successfully" not in result: 113 raise AppInstallError( 114 "Failed to install %s on %s. Reason:%s" % 115 (package, device.__get_serial__(), result)) 116 LOG.debug("Installed package finished {}".format(package)) 117 elif command_type == "uninstall": 118 LOG.debug("Trying to uninstall package {}".format(command_value)) 119 package = get_file_absolute_path(command_value, self.paths) 120 app_name = get_app_name_by_tool(package, self.paths) 121 if app_name: 122 result = device.uninstall_package(app_name) 123 if not result.startswith("Success"): 124 LOG.error("error uninstalling package %s %s" % 125 (device.__get_serial__(), result)) 126 LOG.debug("uninstall package finished {}".format(app_name)) 127 elif command_type == "pull": 128 files = command_value.split("->") 129 remote = files[0].strip() 130 local = files[1].strip() 131 device.pull_file(remote, local) 132 elif command_type == "push": 133 files = command_value.split("->") 134 src = files[0].strip() 135 dst = files[1].strip() if files[1].strip().startswith("/") else \ 136 files[1].strip() + Props.dest_root 137 LOG.debug( 138 "Trying to push the file local {} to remote{}".format( 139 src, dst)) 140 real_src_path = get_file_absolute_path(src, self.paths) 141 if not real_src_path or not os.path.exists(real_src_path): 142 LOG.error( 143 "The src file {} to be pushed does not exist".format(src)) 144 device.push_file(real_src_path, dst) 145 LOG.debug("Push file finished from {} to {}".format(src, dst)) 146 elif command_type == "shell": 147 device.execute_shell_command(command_value) 148 149 150@Plugin(type=Plugin.TEST_KIT, id=CKit.sts) 151class STSKit(ITestKit): 152 def __init__(self): 153 self.sts_version = "" 154 self.throw_error = "" 155 156 def __check_config__(self, config): 157 self.sts_version = get_config_value('sts-version', config) 158 self.throw_error = get_config_value('throw-error', config) 159 if len(self.sts_version) < 1: 160 raise TypeError( 161 "The sts_version: {} is invalid".format(self.sts_version)) 162 163 def __setup__(self, device, **kwargs): 164 del kwargs 165 LOG.debug("STSKit setup, device:{}, params:{}". 166 format(device, self.get_plugin_config().__dict__)) 167 device_spl = device.get_property(Props.security_patch) 168 if device_spl is None or device_spl == "": 169 LOG.error("The device security {} is invalid".format(device_spl)) 170 raise ParamError( 171 "The device security patch version {} is invalid".format( 172 device_spl)) 173 rex = '^[a-zA-Z\\d\\.]+_([\\d]+-[\\d]+)$' 174 match = re.match(rex, self.sts_version) 175 if match is None: 176 LOG.error("The sts version {} does match the rule".format( 177 self.sts_version)) 178 raise ParamError("The sts version {} does match the rule".format( 179 self.sts_version)) 180 sts_version_date_user = match.group(1).join("-01") 181 sts_version_date_kernel = match.group(1).join("-05") 182 if device_spl in [sts_version_date_user, sts_version_date_kernel]: 183 LOG.info( 184 "The device SPL version {} match the sts version {}".format( 185 device_spl, self.sts_version)) 186 else: 187 err_msg = "The device SPL version {} does not match the sts " \ 188 "version {}".format(device_spl, self.sts_version) 189 LOG.error(err_msg) 190 raise ParamError(err_msg) 191 192 def __teardown__(self, device): 193 LOG.debug("STSKit teardown: device:{}, params:{}".format 194 (device, self.get_plugin_config().__dict__)) 195 196 197@Plugin(type=Plugin.TEST_KIT, id=CKit.push) 198class PushKit(ITestKit): 199 def __init__(self): 200 self.pre_push = "" 201 self.push_list = "" 202 self.post_push = "" 203 self.is_uninstall = "" 204 self.paths = "" 205 self.pushed_file = [] 206 self.abort_on_push_failure = True 207 self.teardown_push = "" 208 209 def __check_config__(self, config): 210 self.pre_push = get_config_value('pre-push', config) 211 self.push_list = get_config_value('push', config) 212 self.post_push = get_config_value('post-push', config) 213 self.teardown_push = get_config_value('teardown-push', config) 214 self.is_uninstall = get_config_value('uninstall', config, 215 is_list=False, default=True) 216 self.abort_on_push_failure = get_config_value( 217 'abort-on-push-failure', config, is_list=False, default=True) 218 if isinstance(self.abort_on_push_failure, str): 219 self.abort_on_push_failure = False if \ 220 self.abort_on_push_failure.lower() == "false" else True 221 222 self.paths = get_config_value('paths', config) 223 self.pushed_file = [] 224 225 def __setup__(self, device, **kwargs): 226 del kwargs 227 LOG.debug("PushKit setup, device:{}".format(device.device_sn)) 228 for command in self.pre_push: 229 run_command(device, command) 230 dst = None 231 remount(device) 232 for push_info in self.push_list: 233 files = re.split('->|=>', push_info) 234 if len(files) != 2: 235 LOG.error("The push spec is invalid: {}".format(push_info)) 236 continue 237 src = files[0].strip() 238 dst = files[1].strip() if files[1].strip().startswith("/") else \ 239 files[1].strip() + Props.dest_root 240 LOG.debug( 241 "Trying to push the file local {} to remote {}".format(src, 242 dst)) 243 244 try: 245 real_src_path = get_file_absolute_path(src, self.paths) 246 except ParamError as error: 247 if self.abort_on_push_failure: 248 raise error 249 else: 250 LOG.warning(error, error_no=error.error_no) 251 continue 252 # hdc don't support push directory now 253 if os.path.isdir(real_src_path): 254 device.connector_command("shell mkdir {}".format(dst)) 255 for root, _, files in os.walk(real_src_path): 256 for file in files: 257 device.push_file("{}".format(os.path.join(root, file)), 258 "{}".format(dst)) 259 LOG.debug( 260 "Push file finished from {} to {}".format( 261 os.path.join(root, file), dst)) 262 self.pushed_file.append(os.path.join(dst, file)) 263 else: 264 if device.is_directory(dst): 265 dst = os.path.join(dst, os.path.basename(real_src_path)) 266 if dst.find("\\") > -1: 267 dst_paths = dst.split("\\") 268 dst = "/".join(dst_paths) 269 device.push_file("{}".format(real_src_path), 270 "{}".format(dst)) 271 LOG.debug("Push file finished from {} to {}".format(src, dst)) 272 self.pushed_file.append(dst) 273 for command in self.post_push: 274 run_command(device, command) 275 return self.pushed_file, dst 276 277 def add_pushed_dir(self, src, dst): 278 for root, _, files in os.walk(src): 279 for file_path in files: 280 self.pushed_file.append( 281 os.path.join(root, file_path).replace(src, dst)) 282 283 def __teardown__(self, device): 284 LOG.debug("PushKit teardown: device:{}".format(device.device_sn)) 285 for command in self.teardown_push: 286 run_command(device, command) 287 if self.is_uninstall: 288 remount(device) 289 for file_name in self.pushed_file: 290 LOG.debug("Trying to remove file {}".format(file_name)) 291 file_name = file_name.replace("\\", "/") 292 293 for _ in range( 294 Props.trying_remove_maximum_times): 295 collect_receiver = CollectingOutputReceiver() 296 file_name = check_path_legal(file_name) 297 device.execute_shell_command("rm -rf {}".format( 298 file_name), receiver=collect_receiver, 299 output_flag=False) 300 if not collect_receiver.output: 301 LOG.debug( 302 "Removed file {} successfully".format(file_name)) 303 break 304 else: 305 LOG.error("Removed file {} successfully". 306 format(collect_receiver.output)) 307 else: 308 LOG.error("Failed to remove file {}".format(file_name)) 309 310 def __add_pushed_file__(self, device, src, dst): 311 if device.is_directory(dst): 312 dst = dst + os.path.basename(src) if dst.endswith( 313 "/") else dst + "/" + os.path.basename(src) 314 self.pushed_file.append(dst) 315 316 def __add_dir_pushed_files__(self, device, src, dst): 317 if device.file_exist(device, dst): 318 for _, dirs, files in os.walk(src): 319 for file_path in files: 320 if dst.endswith("/"): 321 dst = "%s%s" % (dst, os.path.basename(file_path)) 322 else: 323 dst = "%s/%s" % (dst, os.path.basename(file_path)) 324 self.pushed_file.append(dst) 325 for dir_name in dirs: 326 self.__add_dir_pushed_files__(device, dir_name, dst) 327 else: 328 self.pushed_file.append(dst) 329 330 331@Plugin(type=Plugin.TEST_KIT, id=CKit.propertycheck) 332class PropertyCheckKit(ITestKit): 333 def __init__(self): 334 self.prop_name = "" 335 self.expected_value = "" 336 self.throw_error = "" 337 338 def __check_config__(self, config): 339 self.prop_name = get_config_value('property-name', config, 340 is_list=False) 341 self.expected_value = get_config_value('expected-value', config, 342 is_list=False) 343 self.throw_error = get_config_value('throw-error', config, 344 is_list=False) 345 346 def __setup__(self, device, **kwargs): 347 del kwargs 348 LOG.debug("PropertyCheckKit setup, device:{}".format(device.device_sn)) 349 if not self.prop_name: 350 LOG.warning("The option of property-name not setting") 351 return 352 prop_value = device.get_property(self.prop_name) 353 if not prop_value: 354 LOG.warning( 355 "The property {} not found on device, cannot check the value". 356 format(self.prop_name)) 357 return 358 359 if prop_value != self.expected_value: 360 msg = "The value found for property {} is {}, not same with the " \ 361 "expected {}".format(self.prop_name, prop_value, 362 self.expected_value) 363 LOG.warning(msg) 364 if self.throw_error and self.throw_error.lower() == 'true': 365 raise Exception(msg) 366 367 @classmethod 368 def __teardown__(cls, device): 369 LOG.debug("PropertyCheckKit teardown: device:{}".format( 370 device.device_sn)) 371 372 373@Plugin(type=Plugin.TEST_KIT, id=CKit.shell) 374class ShellKit(ITestKit): 375 def __init__(self): 376 self.command_list = [] 377 self.tear_down_command = [] 378 self.paths = None 379 380 def __check_config__(self, config): 381 self.command_list = get_config_value('run-command', config) 382 self.tear_down_command = get_config_value('teardown-command', config) 383 self.tear_down_local_command = get_config_value('teardown-localcommand', config) 384 self.paths = get_config_value('paths', config) 385 386 def __setup__(self, device, **kwargs): 387 del kwargs 388 LOG.debug("ShellKit setup, device:{}".format(device.device_sn)) 389 if len(self.command_list) == 0: 390 LOG.info("No setup_command to run, skipping!") 391 return 392 for command in self.command_list: 393 run_command(device, command) 394 395 def __teardown__(self, device): 396 LOG.debug("ShellKit teardown: device:{}".format(device.device_sn)) 397 if len(self.tear_down_command) == 0: 398 LOG.info("No teardown_command to run, skipping!") 399 else: 400 for command in self.tear_down_command: 401 run_command(device, command) 402 if len(self.tear_down_local_command) == 0: 403 LOG.info("No teardown-localcommand to run, skipping!") 404 else: 405 for command in self.tear_down_local_command: 406 subprocess.run(command) 407 408 409@Plugin(type=Plugin.TEST_KIT, id=CKit.wifi) 410class WifiKit(ITestKit): 411 def __init__(self): 412 self.certfilename = "" 413 self.certpassword = "" 414 self.wifiname = "" 415 self.paths = "" 416 417 def __check_config__(self, config): 418 self.certfilename = get_config_value( 419 'certfilename', config, False, 420 default=None) 421 self.certpassword = get_config_value( 422 'certpassword', config, False, 423 default=None) 424 self.wifiname = get_config_value( 425 'wifiname', config, False, 426 default=None) 427 self.paths = get_config_value('paths', config) 428 429 def __setup__(self, device, **kwargs): 430 request = kwargs.get("request", None) 431 if not request: 432 LOG.error("WifiKit need input request") 433 return 434 testargs = request.get("testargs", {}) 435 self.certfilename = \ 436 testargs.pop("certfilename", [self.certfilename])[0] 437 self.wifiname = \ 438 testargs.pop("wifiname", [self.wifiname])[0] 439 self.certpassword = \ 440 testargs.pop("certpassword", [self.certpassword])[0] 441 del kwargs 442 LOG.debug("WifiKit setup, device:{}".format(device.device_sn)) 443 444 try: 445 wifi_app_path = get_file_absolute_path( 446 Props.Paths.service_wifi_app_path, self.paths) 447 except ParamError as _: 448 wifi_app_path = None 449 450 if wifi_app_path is None: 451 LOG.error("The resource wifi app file does not exist!") 452 return 453 454 try: 455 pfx_path = get_file_absolute_path( 456 "tools/wifi/%s" % self.certfilename 457 ) if self.certfilename else None 458 except ParamError as _: 459 pfx_path = None 460 461 if pfx_path is None: 462 LOG.error("The resource wifi pfx file does not exist!") 463 return 464 pfx_dest_path = \ 465 "/storage/emulated/0/%s" % self.certfilename 466 if self.wifiname is None: 467 LOG.error("The wifi name is not given!") 468 return 469 if self.certpassword is None: 470 LOG.error("The wifi password is not given!") 471 return 472 473 device.install_package(wifi_app_path, command="-r") 474 device.push_file(pfx_path, pfx_dest_path) 475 device.execute_shell_command("svc wifi enable") 476 for _ in range(Props.maximum_connect_wifi_times): 477 connect_wifi_cmd = Props.connect_wifi_cmd % ( 478 pfx_dest_path, 479 self.certpassword, 480 self.wifiname 481 ) 482 if device.execute_shell_command(connect_wifi_cmd): 483 LOG.info("Connect wifi successfully") 484 break 485 else: 486 LOG.error("Connect wifi failed") 487 488 @classmethod 489 def __teardown__(cls, device): 490 LOG.debug("WifiKit teardown: device:{}".format(device.device_sn)) 491 LOG.info("Disconnect wifi") 492 device.execute_shell_command("svc wifi disable") 493 494 495@dataclass 496class Props: 497 @dataclass 498 class Paths: 499 system_build_prop_path = "/%s/%s" % ("system", "build.prop") 500 service_wifi_app_path = "tools/wifi/%s" % "Service-wifi.app" 501 502 dest_root = "/%s/%s/" % ("data", "data") 503 mnt_external_storage = "EXTERNAL_STORAGE" 504 trying_remove_maximum_times = 3 505 maximum_connect_wifi_times = 3 506 connect_wifi_cmd = "am instrument -e request \"{module:Wifi, " \ 507 "method:connectWifiByCertificate, params:{'certPath':" \ 508 "'%s'," \ 509 "'certPassword':'%s'," \ 510 "'wifiName':'%s'}}\" " \ 511 "-w com.xdeviceservice.service/.MainInstrumentation" 512 security_patch = "ro.build.version.security_patch" 513 514 515@Plugin(type=Plugin.TEST_KIT, id=CKit.config) 516class ConfigKit(ITestKit): 517 def __init__(self): 518 self.is_connect_wifi = "" 519 self.is_disconnect_wifi = "" 520 self.wifi_kit = WifiKit() 521 self.min_external_store_space = "" 522 self.is_disable_dialing = "" 523 self.is_test_harness = "" 524 self.is_audio_silent = "" 525 self.is_disable_dalvik_verifier = "" 526 self.build_prop_list = "" 527 self.is_enable_hook = "" 528 self.cust_prop_file = "" 529 self.is_prop_changed = False 530 self.local_system_prop_file = "" 531 self.cust_props = "" 532 self.is_reboot_delay = "" 533 self.is_remount = "" 534 self.local_cust_prop_file = {} 535 536 def __check_config__(self, config): 537 self.is_connect_wifi = get_config_value('connect-wifi', config, 538 is_list=False, default=False) 539 self.is_disconnect_wifi = get_config_value( 540 'disconnect-wifi-after-test', config, is_list=False, default=True) 541 self.wifi_kit = WifiKit() 542 self.min_external_store_space = get_config_value( 543 'min-external-store-space', config) 544 self.is_disable_dialing = get_config_value('disable-dialing', config) 545 self.is_test_harness = get_config_value('set-test-harness', config) 546 self.is_audio_silent = get_config_value('audio-silent', config) 547 self.is_disable_dalvik_verifier = get_config_value( 548 'disable-dalvik-verifier', config) 549 self.build_prop_list = get_config_value('build-prop', config) 550 self.cust_prop_file = get_config_value('cust-prop-file', config) 551 self.cust_props = get_config_value('cust-prop', config) 552 self.is_enable_hook = get_config_value('enable-hook', config) 553 self.is_reboot_delay = get_config_value('reboot-delay', config) 554 self.is_remount = get_config_value('remount', config, default=True) 555 self.local_system_prop_file = NamedTemporaryFile(prefix='build', 556 suffix='.prop', 557 delete=False).name 558 559 def __setup__(self, device, **kwargs): 560 del kwargs 561 LOG.debug("ConfigKit setup, device:{}".format(device.device_sn)) 562 if self.is_remount: 563 remount(device) 564 self.is_prop_changed = self.modify_system_prop(device) 565 self.is_prop_changed = self.modify_cust_prop( 566 device) or self.is_prop_changed 567 568 keep_screen_on(device) 569 if self.is_enable_hook: 570 pass 571 if self.is_prop_changed: 572 device.reboot() 573 574 def __teardown__(self, device): 575 LOG.debug("ConfigKit teardown: device:{}".format(device.device_sn)) 576 if self.is_remount: 577 remount(device) 578 if self.is_connect_wifi and self.is_disconnect_wifi: 579 self.wifi_kit.__teardown__(device) 580 if self.is_prop_changed: 581 device.push_file(self.local_system_prop_file, 582 Props.Paths.system_build_prop_path) 583 device.execute_shell_command( 584 " ".join(["chmod 644", Props.Paths.system_build_prop_path])) 585 os.remove(self.local_system_prop_file) 586 587 for target_file, temp_file in self.local_cust_prop_file.items(): 588 device.push_file(temp_file, target_file) 589 device.execute_shell_command( 590 " ".join(["chmod 644", target_file])) 591 os.remove(temp_file) 592 593 def modify_system_prop(self, device): 594 prop_changed = False 595 new_props = {} 596 if self.is_disable_dialing: 597 new_props['ro.telephony.disable-call'] = 'true' 598 if self.is_test_harness: 599 new_props['ro.monkey'] = '1' 600 new_props['ro.test_harness'] = '1' 601 if self.is_audio_silent: 602 new_props['ro.audio.silent'] = '1' 603 if self.is_disable_dalvik_verifier: 604 new_props['dalvik.vm.dexopt-flags'] = 'v=n' 605 for prop in self.build_prop_list: 606 if prop is None or prop.find("=") < 0 or len(prop.split("=")) != 2: 607 LOG.warning("The build prop:{} not match the format " 608 "'key=value'".format(prop)) 609 continue 610 new_props[prop.split("=")[0]] = prop.split("=")[1] 611 if new_props: 612 prop_changed = modify_props(device, self.local_system_prop_file, 613 Props.Paths.system_build_prop_path, 614 new_props) 615 return prop_changed 616 617 def modify_cust_prop(self, device): 618 prop_changed = False 619 cust_files = {} 620 new_props = {} 621 for cust_prop_file in self.cust_prop_file: 622 # the correct format should be "CustName:/cust/prop/absolutepath" 623 if len(cust_prop_file.split(":")) != 2: 624 LOG.error( 625 "The value %s of option cust-prop-file is incorrect" % 626 cust_prop_file) 627 continue 628 cust_files[cust_prop_file.split(":")[0]] = \ 629 cust_prop_file.split(":")[1] 630 for prop in self.cust_props: 631 # the correct format should be "CustName:key=value" 632 prop_infos = re.split(r'[:|=]', prop) 633 if len(prop_infos) != 3: 634 LOG.error( 635 "The value {} of option cust-prop is incorrect".format( 636 prop)) 637 continue 638 file_name, key, value = prop_infos 639 if file_name not in cust_files: 640 LOG.error( 641 "The custName {} must be in cust-prop-file option".format( 642 file_name)) 643 continue 644 props = new_props.setdefault(file_name, {}) 645 props[key] = value 646 647 for name in new_props.keys(): 648 cust_file = cust_files.get(name) 649 temp_cust_file = NamedTemporaryFile(prefix='cust', suffix='.prop', 650 delete=False).name 651 self.local_cust_prop_file[cust_file] = temp_cust_file 652 try: 653 prop_changed = modify_props(device, temp_cust_file, cust_file, 654 new_props[name]) or prop_changed 655 except KeyError: 656 LOG.error("Get props error.") 657 continue 658 659 return prop_changed 660 661 662@Plugin(type=Plugin.TEST_KIT, id=CKit.app_install) 663class AppInstallKit(ITestKit): 664 def __init__(self): 665 self.app_list = "" 666 self.app_list_name = "" 667 self.is_clean = "" 668 self.alt_dir = "" 669 self.ex_args = "" 670 self.installed_app = set() 671 self.paths = "" 672 self.is_pri_app = "" 673 self.pushed_hap_file = set() 674 self.env_index_list = None 675 676 def __check_config__(self, options): 677 self.app_list = get_config_value('test-file-name', options) 678 self.app_list_name = get_config_value('test-file-packName', options) 679 self.is_clean = get_config_value('cleanup-apps', options, False) 680 self.alt_dir = get_config_value('alt-dir', options, False) 681 if self.alt_dir and self.alt_dir.startswith("resource/"): 682 self.alt_dir = self.alt_dir[len("resource/"):] 683 self.ex_args = get_config_value('install-arg', options, False) 684 self.installed_app = set() 685 self.paths = get_config_value('paths', options) 686 self.is_pri_app = get_config_value('install-as-privapp', options, 687 False, default=False) 688 self.env_index_list = get_config_value('env-index', options) 689 690 def __setup__(self, device, **kwargs): 691 del kwargs 692 LOG.debug("AppInstallKit setup, device:{}".format(device.device_sn)) 693 if len(self.app_list) == 0: 694 LOG.info("No app to install, skipping!") 695 return 696 for app in self.app_list: 697 if self.alt_dir: 698 app_file = get_file_absolute_path(app, self.paths, 699 self.alt_dir) 700 else: 701 app_file = get_file_absolute_path(app, self.paths) 702 if app_file is None: 703 LOG.error("The app file {} does not exist".format(app)) 704 continue 705 device.connector_command("install \"{}\"".format(app_file)) 706 self.installed_app.add(app_file) 707 708 def __teardown__(self, device): 709 LOG.debug("AppInstallKit teardown: device:{}".format(device.device_sn)) 710 if self.is_clean and str(self.is_clean).lower() == "true": 711 if self.app_list_name and len(self.app_list_name) > 0: 712 for app_name in self.app_list_name: 713 result = device.uninstall_package(app_name) 714 if result and (result.startswith("Success") or "successfully" in result): 715 LOG.debug("uninstalling package Success. result is %s" % 716 result) 717 else: 718 LOG.warning("Error uninstalling package %s %s" % 719 (device.__get_serial__(), result)) 720 else: 721 for app in self.installed_app: 722 app_name = get_app_name(app) 723 if app_name: 724 result = device.uninstall_package(app_name) 725 if result and (result.startswith("Success") or "successfully" in result): 726 LOG.debug("uninstalling package Success. result is %s" % 727 result) 728 else: 729 LOG.warning("Error uninstalling package %s %s" % 730 (device.__get_serial__(), result)) 731 else: 732 LOG.warning("Can't find app name for %s" % app) 733 if self.is_pri_app: 734 remount(device) 735 for pushed_file in self.pushed_hap_file: 736 device.execute_shell_command("rm -r %s" % pushed_file) 737 738 def install_hap(self, device, hap_file): 739 if self.is_pri_app: 740 LOG.info("Install hap as privileged app {}".format(hap_file)) 741 hap_name = os.path.basename(hap_file).replace(".hap", "") 742 try: 743 with TemporaryDirectory(prefix=hap_name) as temp_dir: 744 zif_file = zipfile.ZipFile(hap_file) 745 zif_file.extractall(path=temp_dir) 746 entry_app = os.path.join(temp_dir, "Entry.app") 747 push_dest_dir = os.path.join("/system/priv-app/", hap_name) 748 device.execute_shell_command("rm -rf " + push_dest_dir, 749 output_flag=False) 750 device.push_file(entry_app, os.path.join( 751 push_dest_dir + os.path.basename(entry_app))) 752 device.push_file(hap_file, os.path.join( 753 push_dest_dir + os.path.basename(hap_file))) 754 self.pushed_hap_file.add(os.path.join( 755 push_dest_dir + os.path.basename(hap_file))) 756 device.reboot() 757 except RuntimeError as exception: 758 msg = "Install hap app failed withe error {}".format(exception) 759 LOG.error(msg) 760 raise Exception(msg) 761 except Exception as exception: 762 msg = "Install hap app failed withe exception {}".format( 763 exception) 764 LOG.error(msg) 765 raise Exception(msg) 766 finally: 767 zif_file.close() 768 else: 769 push_dest = "/%s" % "sdcard" 770 push_dest = "%s/%s" % (push_dest, os.path.basename(hap_file)) 771 device.push_file(hap_file, push_dest) 772 self.pushed_hap_file.add(push_dest) 773 output = device.execute_shell_command("bm install -p " + push_dest) 774 if not output.startswith("Success") and not "successfully" in output: 775 output = output.strip() 776 if "[ERROR_GET_BUNDLE_INSTALLER_FAILED]" not in output.upper(): 777 raise AppInstallError( 778 "Failed to install %s on %s. Reason:%s" % 779 (push_dest, device.__get_serial__(), output)) 780 else: 781 LOG.info("'[ERROR_GET_BUNDLE_INSTALLER_FAILED]' occurs, " 782 "retry install hap") 783 exec_out = self.retry_install_hap( 784 device, "bm install -p " + push_dest) 785 if not exec_out.startswith("Success") and not "successfully" in output: 786 raise AppInstallError( 787 "Retry failed,Can't install %s on %s. Reason:%s" % 788 (push_dest, device.__get_serial__(), exec_out)) 789 else: 790 LOG.debug("Install %s success" % push_dest) 791 792 @classmethod 793 def retry_install_hap(cls, device, command): 794 real_command = [HdcHelper.CONNECTOR_NAME, "-t", str(device.device_sn), "-s", 795 "tcp:%s:%s" % (str(device.host), str(device.port)), 796 "shell", command] 797 message = "%s execute command: %s" % \ 798 (convert_serial(device.device_sn), " ".join(real_command)) 799 LOG.info(message) 800 exec_out = "" 801 for wait_count in range(1, MAX_WAIT_COUNT): 802 LOG.debug("Retry times:%s, wait %ss" % 803 (wait_count, (wait_count * 10))) 804 time.sleep(wait_count * 10) 805 exec_out = exec_cmd(real_command) 806 if exec_out and exec_out.startswith("Success"): 807 break 808 if not exec_out: 809 exec_out = "System is not in %s" % ["Windows", "Linux", "Darwin"] 810 LOG.info("Retry install hap result is: [%s]" % exec_out.strip()) 811 return exec_out 812 813 814@Plugin(type=Plugin.TEST_KIT, id=CKit.component) 815class ComponentKit(ITestKit): 816 817 def __init__(self): 818 self._white_list_file = "" 819 self._white_list = "" 820 self._cap_file = "" 821 self.paths = "" 822 self.cache_subsystem = set() 823 self.cache_part = set() 824 825 def __check_config__(self, config): 826 self._white_list_file =\ 827 get_config_value('white-list', config, is_list=False) 828 self._cap_file = get_config_value('cap-file', config, is_list=False) 829 self.paths = get_config_value('paths', config) 830 831 def __setup__(self, device, **kwargs): 832 if hasattr(device, ConfigConst.support_component): 833 return 834 if device.label in ["phone", "watch", "car", "tv", "tablet", "ivi"]: 835 command = "cat %s" % self._cap_file 836 result = device.execute_shell_command(command) 837 part_set = set() 838 subsystem_set = set() 839 if "{" in result: 840 for item in json.loads(result).get("components", []): 841 part_set.add(item.get("component", "")) 842 subsystems, parts = self.get_white_list() 843 part_set.update(parts) 844 subsystem_set.update(subsystems) 845 setattr(device, ConfigConst.support_component, 846 (subsystem_set, part_set)) 847 self.cache_subsystem.update(subsystem_set) 848 self.cache_part.update(part_set) 849 850 def get_cache(self): 851 return self.cache_subsystem, self.cache_part 852 853 def get_white_list(self): 854 if not self._white_list and self._white_list_file: 855 self._white_list = self._parse_white_list() 856 return self._white_list 857 858 def _parse_white_list(self): 859 subsystem = set() 860 part = set() 861 white_json_file = os.path.normpath(self._white_list_file) 862 if not os.path.isabs(white_json_file): 863 white_json_file = \ 864 get_file_absolute_path(white_json_file, self.paths) 865 if os.path.isfile(white_json_file): 866 subsystem_list = list() 867 flags = os.O_RDONLY 868 modes = stat.S_IWUSR | stat.S_IRUSR 869 with os.fdopen(os.open(white_json_file, flags, modes), 870 "r") as file_content: 871 json_result = json.load(file_content) 872 if "subsystems" in json_result.keys(): 873 subsystem_list.extend(json_result["subsystems"]) 874 for subsystem_item_list in subsystem_list: 875 for key, value in subsystem_item_list.items(): 876 if key == "subsystem": 877 subsystem.add(value) 878 elif key == "components": 879 for component_item in value: 880 if "component" in component_item.keys(): 881 part.add( 882 component_item["component"]) 883 884 return subsystem, part 885 886 def __teardown__(self, device): 887 if hasattr(device, ConfigConst.support_component): 888 setattr(device, ConfigConst.support_component, None) 889 self._white_list_file = "" 890 self._white_list = "" 891 self._cap_file = "" 892 self.cache_subsystem.clear() 893 self.cache_part.clear() 894 self.cache_device.clear() 895 896 897@Plugin(type=Plugin.TEST_KIT , id=CKit.permission) 898class PermissionKit(ITestKit): 899 def __init__(self): 900 self.package_name_list = None 901 self.permission_list = None 902 903 def __check_config__(self, config): 904 self.package_name_list = \ 905 get_config_value('package-names', config, True, []) 906 self.permission_list = \ 907 get_config_value('permissions', config, True, []) 908 909 def __setup__(self, device, **kwargs): 910 if not self.package_name_list or not self.permission_list: 911 LOG.warning("Please check parameters of permission kit in json") 912 return 913 for index in range(len(self.package_name_list)): 914 cur_name = self.package_name_list[index] 915 token_id = self._get_token_id(device, cur_name) 916 if not token_id: 917 LOG.warning("Not found accessTokenId of '{}'".format(cur_name)) 918 continue 919 for permission in self.permission_list[index]: 920 command = "atm perm -g -i {} -p {}".format(token_id, 921 permission) 922 out = device.execute_shell_command(command) 923 LOG.debug("Set permission result: {}".format(out)) 924 925 def __teardown__(self, device): 926 pass 927 928 def _get_token_id(self, device, pkg_name): 929 # shell bm dump -n 930 dump_command = "bm dump -n {}".format(pkg_name) 931 content = device.execute_shell_command(dump_command) 932 if not content or not str(content).startswith(pkg_name): 933 return "" 934 content = content[len(pkg_name) + len(":\n"):] 935 dump_dict = json.loads(content) 936 if "userInfo" not in dump_dict.keys(): 937 return "" 938 user_info_dict = dump_dict["userInfo"][0] 939 if "accessTokenId" not in user_info_dict.keys(): 940 return "" 941 else: 942 return user_info_dict["accessTokenId"] 943 944 945def keep_screen_on(device): 946 device.execute_shell_command("svc power stayon true") 947 948 949def run_command(device, command): 950 LOG.debug("The command:{} is running".format(command)) 951 stdout = None 952 if command.strip() == "remount": 953 remount(device) 954 elif command.strip() == "reboot": 955 device.reboot() 956 elif command.strip() == "reboot-delay": 957 pass 958 elif command.strip().endswith("&"): 959 device.execute_shell_in_daemon(command.strip()) 960 else: 961 stdout = device.execute_shell_command(command) 962 LOG.debug("Run command result: %s" % (stdout if stdout else "")) 963 return stdout 964 965 966def junit_dex_para_parse(device, junit_paras, prefix_char="--"): 967 """To parse the para of junit 968 Args: 969 device: the device running 970 junit_paras: the para dict of junit 971 prefix_char: the prefix char of parsed cmd 972 Returns: 973 the new para using in a command like -e testFile xxx 974 -e coverage true... 975 """ 976 ret_str = [] 977 path = "/%s/%s/%s" % ("data", "local", "ajur") 978 include_file = "%s/%s" % (path, "includes.txt") 979 exclude_file = "%s/%s" % (path, "excludes.txt") 980 981 if not isinstance(junit_paras, dict): 982 LOG.warning("The para of junit is not the dict format as required") 983 return "" 984 # Disable screen keyguard 985 disable_key_guard = junit_paras.get('disable-keyguard') 986 if not disable_key_guard or disable_key_guard[0].lower() != 'false': 987 disable_keyguard(device) 988 989 for para_name in junit_paras.keys(): 990 path = "/%s/%s/%s/" % ("data", "local", "ajur") 991 if para_name.strip() == 'test-file-include-filter': 992 for file_name in junit_paras[para_name]: 993 device.push_file(file_name, include_file) 994 device.execute_shell_command( 995 'chown -R shell:shell %s' % path) 996 ret_str.append(prefix_char + " ".join(['testFile', include_file])) 997 elif para_name.strip() == "test-file-exclude-filter": 998 for file_name in junit_paras[para_name]: 999 device.push_file(file_name, include_file) 1000 device.execute_shell_command( 1001 'chown -R shell:shell %s' % path) 1002 ret_str.append(prefix_char + " ".join(['notTestFile', 1003 exclude_file])) 1004 elif para_name.strip() == "test" or para_name.strip() == "class": 1005 result = get_class(junit_paras, prefix_char, para_name.strip()) 1006 ret_str.append(result) 1007 elif para_name.strip() == "include-annotation": 1008 ret_str.append(prefix_char + " ".join( 1009 ['annotation', ",".join(junit_paras[para_name])])) 1010 elif para_name.strip() == "exclude-annotation": 1011 ret_str.append(prefix_char + " ".join( 1012 ['notAnnotation', ",".join(junit_paras[para_name])])) 1013 else: 1014 ret_str.append(prefix_char + " ".join( 1015 [para_name, ",".join(junit_paras[para_name])])) 1016 1017 return " ".join(ret_str) 1018 1019 1020def get_app_name(hap_app): 1021 hap_name = os.path.basename(hap_app).replace(".hap", "") 1022 app_name = "" 1023 hap_file_info = None 1024 config_json_file = "" 1025 try: 1026 hap_file_info = zipfile.ZipFile(hap_app) 1027 name_list = ["module.json", "config.json"] 1028 for _, name in enumerate(hap_file_info.namelist()): 1029 if name in name_list: 1030 config_json_file = name 1031 break 1032 config_info = hap_file_info.read(config_json_file).decode('utf-8') 1033 attrs = json.loads(config_info) 1034 if "app" in attrs.keys() and \ 1035 "bundleName" in attrs.get("app", dict()).keys(): 1036 app_name = attrs["app"]["bundleName"] 1037 LOG.info("Obtain the app name {} from json " 1038 "successfully".format(app_name)) 1039 else: 1040 LOG.debug("Tip: 'app' or 'bundleName' not " 1041 "in %s.hap/config.json" % hap_name) 1042 except Exception as e: 1043 LOG.error("get app name from hap error: {}".format(e)) 1044 finally: 1045 if hap_file_info: 1046 hap_file_info.close() 1047 return app_name 1048 1049 1050@Plugin(type=Plugin.TEST_KIT, id=CKit.smartperf) 1051class SmartPerfKit(ITestKit): 1052 def __init__(self): 1053 self._run_command = ["SP_daemon", "-PKG"] 1054 self._process = None 1055 self._pattern = "order:\\d+ (.+)=(.+)" 1056 self._param_key = ["cpu", "gpu", "ddr", "fps", "pnow", "ram", "temp"] 1057 self.target_name = "" 1058 self._msg_queue = None 1059 1060 def __check_config__(self, config): 1061 self._run_command.append(self.target_name) 1062 if isinstance(config, str): 1063 key_value_pairs = str(config).strip(";") 1064 for key_value_pair in key_value_pairs: 1065 key, value = key_value_pair.split(":", 1) 1066 if key == "num": 1067 self._run_command.append("-N") 1068 self._run_command.append(value) 1069 else: 1070 if key in self._param_key and value == "true": 1071 self._run_command.append("-" + key[:1]) 1072 1073 def _execute(self, msg_queue, cmd_list, xls_file, proc_name): 1074 data = [] 1075 while msg_queue.empty(): 1076 process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, 1077 stderr=subprocess.PIPE, 1078 shell=False) 1079 rev = process.stdout.read() 1080 data.append((get_cst_time().strftime("%Y-%m-%d-%H-%M-%S"), rev)) 1081 self.write_to_file(data, proc_name, xls_file) 1082 1083 def write_to_file(self, data, proc_name, xls_file): 1084 from openpyxl import Workbook 1085 from openpyxl import styles 1086 book = Workbook() 1087 sheet = book.active 1088 sheet.row_dimensions[1].height = 30 1089 sheet.column_dimensions["A"].width = 30 1090 sheet.sheet_properties.tabColor = "1072BA" 1091 alignment = styles.Alignment(horizontal='center', vertical='center') 1092 font = styles.Font(size=15, color="000000", bold=True, 1093 italic=False, strike=None, underline=None) 1094 names = ["time", "PKG"] 1095 start = True 1096 for _time, content in data: 1097 cur = [_time, proc_name] 1098 rev_list = str(content, "utf-8").split("\n") 1099 if start: 1100 start = False 1101 for rev in rev_list: 1102 result = re.match(self._pattern, rev) 1103 if result and result.group(1): 1104 names.append(result.group(1)) 1105 cur.append(result.group(2)) 1106 sheet.append(names) 1107 sheet.append(cur) 1108 for pos in range(1, len(names) + 1): 1109 cur_cell = sheet.cell(1, pos) 1110 sheet.column_dimensions[cur_cell.colum_letter].width = 20 1111 cur_cell.alignment = alignment 1112 cur_cell.font = font 1113 else: 1114 for rev in rev_list: 1115 result = re.match(self._pattern, rev) 1116 if result and result.group(1): 1117 cur.append(result.group(2)) 1118 sheet.append(cur) 1119 book.save(xls_file) 1120 1121 def __setup__(self, device, **kwargs): 1122 request = kwargs.get("request") 1123 folder = os.path.join(request.get_config().report_path, "smart_perf") 1124 if not os.path.exists(folder): 1125 os.mkdir(folder) 1126 file = os.path.join(folder, "{}.xlsx".format(request.get_module_name())) 1127 if device.host != "127.0.0.1": 1128 cmd_list = [HdcHelper.CONNECTOR_NAME, "-s", "{}:{}".format( 1129 device.host, device.port), "-t", device.device_sn, "shell"] 1130 else: 1131 cmd_list = [HdcHelper.CONNECTOR_NAME, "-t", device.device_sn, "shell"] 1132 1133 cmd_list.extend(self._run_command) 1134 LOG.debug("Smart perf command:{}".format(" ".join(cmd_list))) 1135 self._msg_queue = Queue() 1136 self._process = Process(target=self._execute, args=( 1137 self._msg_queue, cmd_list, file, self.target_name)) 1138 self._process.start() 1139 1140 def __teardown__(self, device): 1141 if self._process: 1142 if self._msg_queue: 1143 self._msg_queue.put("") 1144 self._msg_queue = None 1145 else: 1146 self._process.terminate() 1147 self._process = None 1148