1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4""" 5Copyright (c) 2021 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17 18Description: Use ark to execute ts/js files 19""" 20 21import os 22import sys 23import re 24import glob 25import argparse 26import subprocess 27import signal 28import time 29import json 30 31DEFAULT_TIMEOUT = 300 32DEFAULT_PGO_THRESHOLD = 10 33TARGET_PLATFORM = ['x64', 'arm64'] 34PRODUCT_LIST = ['hispark_taurus', 'rk3568', 'baltimore'] 35TARGET_PRODUCT_MAP = {'x64': 0, 'arm64': 1} 36 37def parse_args(): 38 parser = argparse.ArgumentParser() 39 parser.add_argument('name', metavar='file|path', type=str, help='test case name: file or path') 40 parser.add_argument('-a', '--all', action='store_true', help='run all test cases on path') 41 parser.add_argument('-p', '--product', metavar='name', 42 help='product name, default is hispark_taurus on x64, rk3568 on arm64') 43 parser.add_argument('-t', '--tool', metavar='opt', 44 help='run tool supported opt: aot, int(c interpreter tool), asmint(asm interpreter tool)') 45 parser.add_argument('-s', '--step', metavar='opt', 46 help='run step supported opt: abc, pack, aot, aotd, run, rund, asmint, asmintd, int, intd') 47 parser.add_argument('-d', '--debug', action='store_true', help='run on debug mode') 48 parser.add_argument('--arm64', action='store_true', help='run on arm64 platform') 49 parser.add_argument('--aot-args', metavar='args', help='pass to aot compiler args') 50 parser.add_argument('--jsvm-args', metavar='args', help='pass to jsvm args') 51 parser.add_argument('-i', '--info', action='store_true', help='add log level of info to args') 52 parser.add_argument('-c', '--clean', action='store_true', help='clean output files') 53 parser.add_argument('--npm', action='store_true', help='npm install') 54 parser.add_argument('--bt', dest='builtin', action='store_true', help='aot compile with lib_ark_builtins.d.ts') 55 parser.add_argument('--pgo', action='store_true', 56 help=f'aot compile with pgo, default threshold is {DEFAULT_PGO_THRESHOLD}') 57 parser.add_argument('--pgo-th', metavar='n', default=DEFAULT_PGO_THRESHOLD, type=int, 58 help=f'pgo hotness threshold, default is {DEFAULT_PGO_THRESHOLD}') 59 parser.add_argument('--timeout', metavar='n', default=DEFAULT_TIMEOUT, type=int, 60 help=f'specify seconds of test timeout, default is {DEFAULT_TIMEOUT}') 61 parser.add_argument('-e', '--env', action='store_true', help='print LD_LIBRARY_PATH') 62 arguments = parser.parse_args() 63 return arguments 64 65def run_command(cmd, timeout=DEFAULT_TIMEOUT): 66 proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) 67 code_format = 'UTF-8' 68 try: 69 (msg, errs) = proc.communicate(timeout=timeout) 70 ret_code = proc.poll() 71 if errs: 72 ret_code = 2 73 except subprocess.TimeoutExpired: 74 proc.kill() 75 proc.terminate() 76 os.kill(proc.pid, signal.SIGTERM) 77 return (1, '', f'exec command timeout {timeout}s') 78 return (ret_code, msg.decode(code_format), errs.decode(code_format)) 79 80def match_list_name(list, name): 81 for str in list: 82 found = str.find(name) 83 if (found == 0): 84 return str 85 return '' 86 87class ArkTest(): 88 def __init__(self, args): 89 self.args = args 90 self.self_dir = os.path.abspath(sys.argv[0]) 91 self.hap_abc = 'ets/modules.abc' 92 self.place_dir = 'arkcompiler/ets_runtime/test' 93 if self.self_dir.find(self.place_dir) < 0: 94 print(f'pls place this script at: {self.place_dir}') 95 sys.exit(1) 96 97 self.ohdir = os.path.abspath(f'{self.self_dir}/../../../..') 98 self.product = PRODUCT_LIST[TARGET_PRODUCT_MAP['x64']] 99 self.builtin = '' 100 if args.builtin: 101 self.builtin = f'{self.ohdir}/arkcompiler/ets_runtime/ecmascript/ts_types/lib_ark_builtins.d' 102 self.arm64 = False 103 if args.step == 'hap': 104 self.arm64 = True 105 if args.arm64: 106 self.product = PRODUCT_LIST[TARGET_PRODUCT_MAP['arm64']] 107 self.arm64 = True 108 if args.product: 109 self.product = match_list_name(PRODUCT_LIST, args.product) 110 self.step = 'all' 111 if args.step: 112 self.step = args.step 113 if args.clean: 114 self.step = 'clean' 115 self.expect = 'expect_output.txt' 116 self.types = {'all': ['.ts', '.js'], 117 'abc': ['.ts', '.js'], 118 'pack': ['.an'], 119 'aot': ['.abc'], 120 'aotd': ['.abc'], 121 'run': ['.abc'], 122 'rund': ['.abc'], 123 'asmint': ['.abc'], 124 'asmintd': ['.abc'], 125 'int': ['.abc'], 126 'intd': ['.abc'], 127 'clean': ['.abc']} 128 129 product_dir = f'{self.ohdir}/out/{self.product}' 130 libs_dir_x64_release = (f'{self.ohdir}/prebuilts/clang/ohos/linux-x86_64/llvm/lib:' 131 f'{product_dir}/clang_x64/arkcompiler/ets_runtime:' 132 f'{product_dir}/clang_x64/thirdparty/icu') 133 libs_dir_x64_debug = (f'{self.ohdir}/prebuilts/clang/ohos/linux-x86_64/llvm/lib:' 134 f'{product_dir}/clang_x64/exe.unstripped/clang_x64/arkcompiler/ets_runtime:' 135 f'{product_dir}/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime:' 136 f'{product_dir}/clang_x64/lib.unstripped/clang_x64/test/test:' 137 f'{product_dir}/clang_x64/lib.unstripped/clang_x64/thirdparty/icu') 138 libs_dir_arm64_release = (f'{self.ohdir}/prebuilts/clang/ohos/linux-x86_64/llvm/lib/aarch64-linux-ohos/c++/:' 139 f'{product_dir}/arkcompiler/ets_runtime/:' 140 f'{product_dir}/utils/utils_base/:' 141 f'{product_dir}/thirdparty/icu:' 142 f'{product_dir}/common/dsoftbus/:' 143 f'{product_dir}/commonlibrary/c_utils:' 144 f'{product_dir}/systemabilitymgr/samgr:' 145 f'{product_dir}/hiviewdfx/hisysevent_native:' 146 f'{product_dir}/common/common:' 147 f'{product_dir}/securec/thirdparty_bounds_checking_function:' 148 f'{product_dir}/hiviewdfx/faultloggerd:' 149 f'{product_dir}/thirdparty/bounds_checking_function:' 150 f'{product_dir}/hiviewdfx/hilog_native:' 151 f'{product_dir}/startup/init:' 152 f'{product_dir}/thirdparty/cjson:' 153 f'{product_dir}/lib.unstripped/common/dsoftbus:' 154 f'{product_dir}/security/selinux:' 155 f'{product_dir}/hiviewdfx/hitrace_native/:' 156 f'{product_dir}/communication/ipc/:' 157 f'{product_dir}/distributedschedule/samgr_standard:' 158 f'{product_dir}/security/access_token:' 159 f'{product_dir}/communication/dsoftbus:' 160 f'{product_dir}/startup/startup_l2/:' 161 f'{product_dir}/security/huks/:' 162 f'{product_dir}/clang_x64/thirdparty/icu/:' 163 f'{product_dir}/clang_x64/arkcompiler/ets_runtime') 164 libs_dir_arm64_debug = (f'{self.ohdir}/prebuilts/clang/ohos/linux-x86_64/llvm/lib/aarch64-linux-ohos/c++/:' 165 f'{product_dir}/lib.unstripped/arkcompiler/ets_runtime/:' 166 f'{product_dir}/utils/utils_base/:' 167 f'{product_dir}/thirdparty/icu:' 168 f'{product_dir}/common/dsoftbus/:' 169 f'{product_dir}/commonlibrary/c_utils:' 170 f'{product_dir}/systemabilitymgr/samgr:' 171 f'{product_dir}/hiviewdfx/hisysevent_native:' 172 f'{product_dir}/common/common:' 173 f'{product_dir}/securec/thirdparty_bounds_checking_function:' 174 f'{product_dir}/hiviewdfx/faultloggerd:' 175 f'{product_dir}/thirdparty/bounds_checking_function:' 176 f'{product_dir}/hiviewdfx/hilog_native:' 177 f'{product_dir}/startup/init:' 178 f'{product_dir}/thirdparty/cjson:' 179 f'{product_dir}/security/selinux:' 180 f'{product_dir}/hiviewdfx/hitrace_native/:' 181 f'{product_dir}/communication/ipc/:' 182 f'{product_dir}/distributedschedule/samgr_standard:' 183 f'{product_dir}/security/access_token:' 184 f'{product_dir}/communication/dsoftbus:' 185 f'{product_dir}/startup/startup_l2/:' 186 f'{product_dir}/security/huks/:' 187 f'{product_dir}/clang_x64/thirdparty/icu/:' 188 f'{product_dir}/clang_x64/arkcompiler/ets_runtime') 189 libs_dir = [[libs_dir_x64_release, libs_dir_x64_debug], [libs_dir_arm64_release, libs_dir_arm64_debug]] 190 bins_dir = [['clang_x64/arkcompiler', 'clang_x64/exe.unstripped/clang_x64/arkcompiler'], 191 ['arkcompiler', 'exe.unstripped/arkcompiler']] 192 icu_arg = f'--icu-data-path={self.ohdir}/third_party/icu/ohos_icu4j/data' 193 self.libs_dir = libs_dir[self.arm64][args.debug] 194 self.compiler = f'{product_dir}/{bins_dir[0][args.debug]}/ets_runtime/ark_aot_compiler' 195 self.jsvm = f'{product_dir}/{bins_dir[self.arm64][args.debug]}/ets_runtime/ark_js_vm' 196 self.ts2abc = f'{product_dir}/clang_x64/arkcompiler/ets_frontend/build/src/index.js' 197 self.builtin 198 self.aot_args = '' 199 self.jsvm_args = icu_arg 200 if self.builtin: 201 self.aot_args = f'{self.aot_args} --builtins-dts={self.builtin}.abc' 202 self.pgo = False 203 if args.pgo: 204 self.pgo = True 205 self.aot_args = (f'{self.aot_args} --enable-pgo-profiler=true --pgo-hotness-threshold={args.pgo_th}' 206 f' --pgo-profiler-path=pgo_file_name.ap') 207 if args.aot_args: 208 self.aot_args = f'{self.aot_args} {args.aot_args}' 209 if args.jsvm_args: 210 self.jsvm_args = f'{self.jsvm_args} {args.jsvm_args}' 211 if args.info: 212 self.aot_args = f'{self.aot_args} --log-level=info' 213 self.jsvm_args = f'{self.jsvm_args} --log-level=info' 214 self.runner = '' 215 self.runnerd = 'gdb --args' 216 if self.arm64: 217 if self.step[:3] != 'aot': 218 self.runner = 'qemu-aarch64' 219 self.runnerd = 'qemu-aarch64 -cpu max,sve=off -g 123456' 220 self.aot_args = f'{self.aot_args} --target-triple=aarch64-unknown-linux-gnu' 221 self.test_count = 0 222 self.fail_cases = [] 223 os.environ['LD_LIBRARY_PATH'] = self.libs_dir 224 if args.env: 225 print(f'export LD_LIBRARY_PATH={self.libs_dir}') 226 sys.exit(0) 227 if args.npm: 228 index_dir = os.path.dirname(self.ts2abc) 229 os.system(f'cd {index_dir}/.. && npm install') 230 sys.exit(0) 231 232 def run_cmd(self, cmd): 233 print(cmd) 234 ret = run_command(cmd, self.args.timeout) 235 if ret[0]: 236 print(ret[2]) 237 return ret 238 239 @staticmethod 240 def get_module_name(hap_dir): 241 with open(f'{hap_dir}/module.json') as f: 242 data = json.load(f) 243 if len(data): 244 return data['module']['name'] 245 else: 246 return 'entry' 247 248 def run_test(self, file): 249 self.test_count += 1 250 basename = os.path.basename(f'{file}') 251 type = os.path.splitext(basename)[-1] 252 name = os.path.splitext(basename)[0] 253 dir = os.path.dirname(file) 254 out_case_dir = f'{dir}' 255 hap_dir = 'null' 256 hap_name = 'null' 257 module_name = 'null' 258 if self.step == 'hap' or self.step == 'pack': 259 hap_dir = os.path.abspath(f'{out_case_dir}/..') 260 hap_name = os.path.basename(hap_dir) 261 module_name = self.get_module_name(hap_dir) 262 abc_file = f'{os.path.splitext(file)[0]}.abc' 263 if self.pgo: 264 pgo_file = f'{hap_dir}/ap/{module_name}' 265 self.aot_args = self.aot_args.replace('pgo_file_name', pgo_file) 266 cmd_map = { 267 'abc': f'node --expose-gc {self.ts2abc} {file} --merge-abc', 268 'pack': [f'mkdir -p {out_case_dir}/../an/arm64-v8a', 269 f'mv {out_case_dir}/{name}.an {hap_dir}/an/arm64-v8a/{module_name}.an', 270 f'mv {out_case_dir}/{name}.ai {hap_dir}/an/arm64-v8a/{module_name}.ai', 271 f'cd {out_case_dir}/.. && rm -f ../{hap_name}.hap && zip -r -q ../{hap_name}.hap *', 272 f'mv {hap_dir}/an/arm64-v8a/{module_name}.an {out_case_dir}/{name}.an', 273 f'mv {hap_dir}/an/arm64-v8a/{module_name}.ai {out_case_dir}/{name}.ai', 274 f'rm -rf {hap_dir}/an'], 275 'aot': f'{self.compiler} {self.aot_args} --aot-file={out_case_dir}/{name} {abc_file}', 276 'aotd': f'{self.runnerd} {self.compiler} {self.aot_args} --aot-file={out_case_dir}/{name} {abc_file}', 277 'run': f'{self.runner} {self.jsvm} {self.jsvm_args} --aot-file={out_case_dir}/{name} --entry-point={name} {abc_file}', 278 'rund': f'{self.runnerd} {self.jsvm} {self.jsvm_args} --aot-file={out_case_dir}/{name} --entry-point={name} {abc_file}', 279 'asmint': f'{self.runner} {self.jsvm} {self.jsvm_args} --entry-point={name} {abc_file}', 280 'asmintd': f'{self.runnerd} {self.jsvm} {self.jsvm_args} --entry-point={name} {abc_file}', 281 'int': f'{self.runner} {self.jsvm} {self.jsvm_args} --asm-interpreter=0 --entry-point={name} {abc_file}', 282 'intd': f'{self.runnerd} {self.jsvm} {self.jsvm_args} --asm-interpreter=0 --entry-point={name} {abc_file}', 283 'clean': f'rm -f {out_case_dir}/{name}.abc {out_case_dir}/{name}.an {out_case_dir}/{name}.ai', 284 'cleanhap': f'rm -rf {hap_dir}/an {out_case_dir}/{name}.an {out_case_dir}/{name}.ai'} 285 if self.builtin: 286 cmd = f'node --expose-gc {self.ts2abc} {self.builtin}.ts -m --merge-abc -q -b' 287 print(cmd) 288 os.system(cmd) 289 if self.step == 'hap': 290 self.step = 'aot' 291 perf_start = time.perf_counter() 292 cmd = cmd_map[self.step] 293 print(cmd) 294 os.system(cmd) 295 perf_end = time.perf_counter() 296 abc_size = os.path.getsize(file) / 1024 297 an_size = os.path.getsize(f'{out_case_dir}/{name}.an') / 1024 298 print(f'test: {file} abc_size: {abc_size: .1f}KB an_size: {an_size:.1f}KB ' 299 f'expand: {an_size / abc_size: .1f} time: {perf_end - perf_start: .1f}s') 300 self.step = 'pack' 301 if self.step == 'pack': 302 for cmd in cmd_map[self.step]: 303 print(cmd) 304 os.system(cmd) 305 print(f'packed hap: {hap_name}.hap') 306 return 307 if self.step == 'clean': 308 if os.path.isfile(f'{hap_dir}/{self.hap_abc}'): 309 self.step = 'cleanhap' 310 if self.step != 'all': 311 # gdb should use the os.system 312 cmd = cmd_map[self.step] 313 print(cmd) 314 if self.arm64 and self.step[-1:] == 'd' and self.step[:3] != 'aot': 315 print(f'gdb-client start: gdb-multiarch {self.jsvm}') 316 print(f'gdb-server connect: target remote:123456') 317 os.system(cmd) 318 return 319 ret = self.run_cmd(cmd_map['abc']) 320 if ret[0]: 321 self.judge_test(file, ret) 322 return 323 if (not self.args.tool) or (self.args.tool == 'aot'): 324 ret = self.run_cmd(cmd_map['aot']) 325 if ret[0] and ret[2].find('aot compile success') < 0: 326 self.judge_test(file, ret) 327 return 328 ret = self.run_cmd(cmd_map['run']) 329 else: 330 ret = self.run_cmd(cmd_map[self.args.tool]) 331 self.judge_test(file, ret) 332 333 def judge_test(self, file, out): 334 if out[0]: 335 self.fail_cases.append(file) 336 print_fail(f'FAIL: {file}') 337 return 338 expect_file = f'{os.path.dirname(file)}/{self.expect}' 339 if os.path.exists(expect_file): 340 with open(expect_file, mode='r') as infile: 341 expect = ''.join(infile.readlines()[13:]) 342 if out[1] != expect: 343 self.fail_cases.append(file) 344 print(f'expect: [{expect}]\nbut got: [{out[1]}]') 345 print_fail(f'FAIL: {file}') 346 else: 347 print_pass(f'PASS: {file}') 348 else: 349 print_pass(f'PASS: {file}') 350 print(out[1]) 351 352 def report_test(self): 353 fail_count = len(self.fail_cases) 354 print(f'Ran tests: {self.test_count}') 355 print(f'Ran failed: {fail_count}') 356 if fail_count == 0: 357 print_pass('================================== All tests Run PASSED!') 358 return 359 print_fail('==================================') 360 for case in self.fail_cases: 361 print(case) 362 print_fail('==================================') 363 364 def find_file(self, dir, postfix_list): 365 result = [] 366 for root, lists, files in os.walk(dir): 367 for file in files: 368 for postfix in postfix_list: 369 path = os.path.join(root, file) 370 found = path.find(postfix) 371 if found == len(path) - len(postfix): 372 result.append(path) 373 if os.path.isfile(dir): 374 for postfix in postfix_list: 375 found = dir.find(postfix) 376 if found == len(dir) - len(postfix): 377 result.append(dir) 378 break 379 return result 380 381 def test_hap(self): 382 if self.step != 'all': 383 return 1 384 files = self.find_file(self.args.name, [self.hap_abc, '.hap']) 385 if len(files): 386 self.step = 'hap' 387 file = files[0] 388 type = os.path.splitext(file)[-1] 389 if type == '.hap': 390 hap_dir = f'{os.path.splitext(file)[0]}.aot' 391 os.system(f'mkdir -p {hap_dir} && unzip -o -q {file} -d {hap_dir}') 392 file = f'{hap_dir}/{self.hap_abc}' 393 self.run_test(file) 394 return 0 395 return 1 396 397 def test(self): 398 # run single test by name 399 files = [] 400 if self.step not in self.types: 401 print(f'not supported step: {self.step}') 402 return 1 403 if not self.args.all: 404 files = self.find_file(self.args.name, self.types[self.step]) 405 if len(files): 406 self.run_test(files[0]) 407 elif self.test_hap(): 408 print(f'input path no test case: {self.args.name}') 409 return 1 410 return 0 411 412 # run all test in path 413 if not os.path.isdir(self.args.name): 414 print(f'input path not exists or is file: {self.args.name}') 415 return 1 416 files = self.find_file(self.args.name, self.types[self.step]) 417 for test in files: 418 self.run_test(test) 419 420 if len(files) == 0: 421 self.test_hap() 422 423 if self.step == 'clean': 424 print('clean output files finished') 425 return 0 426 427 if self.test_count == 0: 428 print(f'input path no test case: {self.args.name}') 429 return 1 430 431 # output report 432 self.report_test() 433 return 0 434 435def print_pass(str): 436 print(f'\033[32;2m{str}\033[0m') 437 sys.stdout.flush() 438 439def print_fail(str): 440 print(f'\033[31;2m{str}\033[0m') 441 sys.stdout.flush() 442 443def main(): 444 args = parse_args() 445 arktest = ArkTest(args) 446 return arktest.test() 447 448if __name__ == '__main__': 449 sys.exit(main()) 450