1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Copyright (c) 2021-2025 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from glob import glob 18from os import path 19from enum import Enum 20import argparse 21import fnmatch 22import multiprocessing 23import os 24import re 25import shutil 26import subprocess 27import sys 28from config import API_VERSION_MAP, ARK_JS_VM_LIST, MIN_SUPPORT_BC_VERSION, MIX_COMPILE_ENTRY_POINT, ES2ABC_API_SUPPORT 29 30 31def is_directory(parser, arg): 32 if not path.isdir(arg): 33 parser.error("The directory '%s' does not exist" % arg) 34 35 return path.abspath(arg) 36 37 38def is_file(parser, arg): 39 if not path.isfile(arg): 40 parser.error("The file '%s' does not exist" % arg) 41 42 return path.abspath(arg) 43 44def prepare_tsc_testcases(test_root): 45 third_party_tsc = path.join(test_root, "TypeScript") 46 ohos_third_party_tsc = path.join(test_root, "../../../../third_party/typescript") 47 48 if not path.isdir(third_party_tsc): 49 if (path.isdir(ohos_third_party_tsc)): 50 return path.abspath(ohos_third_party_tsc) 51 subprocess.run( 52 f"git clone https://gitee.com/openharmony/third_party_typescript.git {third_party_tsc}", 53 shell=True, 54 stdout=subprocess.DEVNULL, 55 ) 56 else: 57 subprocess.run( 58 f"cd {third_party_tsc} && git clean -f > /dev/null 2>&1", 59 shell=True, 60 stdout=subprocess.DEVNULL, 61 ) 62 return third_party_tsc 63 64def check_timeout(value): 65 ivalue = int(value) 66 if ivalue <= 0: 67 raise argparse.ArgumentTypeError( 68 "%s is an invalid timeout value" % value) 69 return ivalue 70 71 72def get_args(): 73 parser = argparse.ArgumentParser(description="Regression test runner") 74 parser.add_argument( 75 'build_dir', type=lambda arg: is_directory(parser, arg), 76 help='panda build directory') 77 parser.add_argument( 78 '--error', action='store_true', dest='error', default=False, 79 help='capture stderr') 80 parser.add_argument( 81 '--abc-to-asm', action='store_true', dest='abc_to_asm', 82 default=False, help='run abc2asm tests') 83 parser.add_argument( 84 '--regression', '-r', action='store_true', dest='regression', 85 default=False, help='run regression tests') 86 parser.add_argument( 87 '--compiler', '-c', action='store_true', dest='compiler', 88 default=False, help='run compiler tests') 89 parser.add_argument( 90 '--tsc', action='store_true', dest='tsc', 91 default=False, help='run tsc tests') 92 parser.add_argument( 93 '--no-progress', action='store_false', dest='progress', default=True, 94 help='don\'t show progress bar') 95 parser.add_argument( 96 '--no-skip', action='store_false', dest='skip', default=True, 97 help='don\'t use skiplists') 98 parser.add_argument( 99 '--update', action='store_true', dest='update', default=False, 100 help='update skiplist') 101 parser.add_argument( 102 '--no-run-gc-in-place', action='store_true', dest='no_gip', default=False, 103 help='enable --run-gc-in-place mode') 104 parser.add_argument( 105 '--filter', '-f', action='store', dest='filter', 106 default="*", help='test filter regexp') 107 parser.add_argument( 108 '--es2panda-timeout', type=check_timeout, 109 dest='es2panda_timeout', default=60, help='es2panda translator timeout') 110 parser.add_argument( 111 '--paoc-timeout', type=check_timeout, 112 dest='paoc_timeout', default=600, help='paoc compiler timeout') 113 parser.add_argument( 114 '--timeout', type=check_timeout, 115 dest='timeout', default=10, help='JS runtime timeout') 116 parser.add_argument( 117 '--gc-type', dest='gc_type', default="g1-gc", help='Type of garbage collector') 118 parser.add_argument( 119 '--aot', action='store_true', dest='aot', default=False, 120 help='use AOT compilation') 121 parser.add_argument( 122 '--no-bco', action='store_false', dest='bco', default=True, 123 help='disable bytecodeopt') 124 parser.add_argument( 125 '--jit', action='store_true', dest='jit', default=False, 126 help='use JIT in interpreter') 127 parser.add_argument( 128 '--arm64-compiler-skip', action='store_true', dest='arm64_compiler_skip', default=False, 129 help='use skiplist for tests failing on aarch64 in AOT or JIT mode') 130 parser.add_argument( 131 '--arm64-qemu', action='store_true', dest='arm64_qemu', default=False, 132 help='launch all binaries in qemu aarch64') 133 parser.add_argument( 134 '--arm32-qemu', action='store_true', dest='arm32_qemu', default=False, 135 help='launch all binaries in qemu arm') 136 parser.add_argument( 137 '--test-list', dest='test_list', default=None, type=lambda arg: is_file(parser, arg), 138 help='run tests listed in file') 139 parser.add_argument( 140 '--aot-args', action='append', dest='aot_args', default=[], 141 help='Additional arguments that will passed to ark_aot') 142 parser.add_argument( 143 '--verbose', '-v', action='store_true', dest='verbose', default=False, 144 help='Enable verbose output') 145 parser.add_argument( 146 '--js-runtime', dest='js_runtime_path', default=None, type=lambda arg: is_directory(parser, arg), 147 help='the path of js vm runtime') 148 parser.add_argument( 149 '--LD_LIBRARY_PATH', dest='ld_library_path', default=None, help='LD_LIBRARY_PATH') 150 parser.add_argument( 151 '--tsc-path', dest='tsc_path', default=None, type=lambda arg: is_directory(parser, arg), 152 help='the path of tsc') 153 parser.add_argument('--hotfix', dest='hotfix', action='store_true', default=False, 154 help='run hotfix tests') 155 parser.add_argument('--hotreload', dest='hotreload', action='store_true', default=False, 156 help='run hotreload tests') 157 parser.add_argument('--coldfix', dest='coldfix', action='store_true', default=False, 158 help='run coldfix tests') 159 parser.add_argument('--coldreload', dest='coldreload', action='store_true', default=False, 160 help='run coldreload tests') 161 parser.add_argument('--base64', dest='base64', action='store_true', default=False, 162 help='run base64 tests') 163 parser.add_argument('--bytecode', dest='bytecode', action='store_true', default=False, 164 help='run bytecode tests') 165 parser.add_argument('--debugger', dest='debugger', action='store_true', default=False, 166 help='run debugger tests') 167 parser.add_argument('--debug', dest='debug', action='store_true', default=False, 168 help='run debug tests') 169 parser.add_argument('--enable-arkguard', action='store_true', dest='enable_arkguard', default=False, 170 help='enable arkguard for compiler tests') 171 parser.add_argument('--aop-transform', dest='aop_transform', action='store_true', default=False, 172 help='run debug tests') 173 parser.add_argument('--version-control', action='store_true', dest='version_control', default=False, 174 help='run api version control tests') 175 176 return parser.parse_args() 177 178 179def run_subprocess_with_beta3(test_obj, cmd): 180 has_target_api = False 181 has_version_12 = False 182 has_sub_version = False 183 is_es2abc_cmd = False 184 185 for param in cmd: 186 if "es2abc" in param: 187 is_es2abc_cmd = True 188 if "--target-api-sub-version" in param: 189 has_sub_version = True 190 if "--target-api-version" in param: 191 has_target_api = True 192 if "12" in param: 193 has_version_12 = True 194 if is_es2abc_cmd and (not has_target_api or (has_version_12 and not has_sub_version)): 195 cmd.append("--target-api-sub-version=beta3") 196 if test_obj: 197 test_obj.log_cmd(cmd) 198 return subprocess.Popen( 199 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 200 201 202class Test: 203 def __init__(self, test_path, flags): 204 self.path = test_path 205 self.flags = flags 206 self.output = None 207 self.error = None 208 self.passed = None 209 self.skipped = None 210 self.reproduce = "" 211 212 def log_cmd(self, cmd): 213 self.reproduce += "\n" + ' '.join(cmd) 214 215 def get_path_to_expected(self): 216 if self.path.find(".d.ts") == -1: 217 return "%s-expected.txt" % (path.splitext(self.path)[0]) 218 return "%s-expected.txt" % (self.path[:self.path.find(".d.ts")]) 219 220 def run(self, runner): 221 test_abc_name = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_") 222 test_abc_path = path.join(runner.build_dir, test_abc_name) 223 cmd = runner.cmd_prefix + [runner.es2panda] 224 cmd.extend(self.flags) 225 cmd.extend(["--output=" + test_abc_path]) 226 cmd.append(self.path) 227 process = run_subprocess_with_beta3(self, cmd) 228 out, err = process.communicate() 229 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 230 231 expected_path = self.get_path_to_expected() 232 try: 233 with open(expected_path, 'r') as fp: 234 expected = fp.read() 235 self.passed = expected == self.output and process.returncode in [ 236 0, 1] 237 except Exception: 238 self.passed = False 239 240 if not self.passed: 241 self.error = err.decode("utf-8", errors="ignore") 242 243 if os.path.exists(test_abc_path): 244 os.remove(test_abc_path) 245 246 return self 247 248 249class TSCTest(Test): 250 def __init__(self, test_path, flags): 251 Test.__init__(self, test_path, flags) 252 self.options = self.parse_options() 253 254 def parse_options(self): 255 test_options = {} 256 257 with open(self.path, "r", encoding="latin1") as f: 258 lines = f.read() 259 options = re.findall(r"//\s?@\w+:.*\n", lines) 260 261 for option in options: 262 separated = option.split(":") 263 opt = re.findall(r"\w+", separated[0])[0].lower() 264 value = separated[1].strip().lower() 265 266 if opt == "filename": 267 if opt in options: 268 test_options[opt].append(value) 269 else: 270 test_options[opt] = [value] 271 272 elif opt == "lib" or opt == "module": 273 test_options[opt] = [each.strip() 274 for each in value.split(",")] 275 elif value == "true" or value == "false": 276 test_options[opt] = value.lower() == "true" 277 else: 278 test_options[opt] = value 279 280 # TODO: Possibility of error: all exports will be catched, even the commented ones 281 if 'module' not in test_options and re.search(r"export ", lines): 282 test_options['module'] = [] 283 284 return test_options 285 286 def run(self, runner): 287 cmd = runner.cmd_prefix + [runner.es2panda, '--parse-only'] 288 cmd.extend(self.flags) 289 if "module" in self.options: 290 cmd.append('--module') 291 cmd.append(self.path) 292 process = run_subprocess_with_beta3(self, cmd) 293 out, err = process.communicate() 294 self.output = out.decode("utf-8", errors="ignore") 295 296 self.passed = True if process.returncode == 0 else False 297 298 if not self.passed: 299 self.error = err.decode("utf-8", errors="ignore") 300 301 return self 302 303 304class TestAop: 305 def __init__(self, cmd, compare_str, compare_abc_str, remove_file): 306 self.cmd = cmd 307 self.compare_str = compare_str 308 self.compare_abc_str = compare_abc_str 309 self.remove_file = remove_file 310 self.path = '' 311 self.output = None 312 self.error = None 313 self.passed = None 314 self.skipped = None 315 self.reproduce = "" 316 317 def log_cmd(self, cmd): 318 self.reproduce += ''.join(["\n", ' '.join(cmd)]) 319 320 def run(self, runner): 321 cmd = self.cmd 322 process = run_subprocess_with_beta3(self, cmd) 323 out, err = process.communicate() 324 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 325 326 if self.compare_str == '': 327 self.passed = True 328 else : 329 self.passed = self.output.startswith(self.compare_str) and process.returncode in [0, 1] 330 if self.remove_file != '' and os.path.exists(self.remove_file): 331 os.remove(self.remove_file) 332 333 if not self.passed: 334 self.error = err.decode("utf-8", errors="ignore") 335 336 abc_path = path.join(os.getcwd(), 'test_aop.abc') 337 if os.path.exists(abc_path): 338 if self.compare_abc_str != '': 339 with open(abc_path, "r") as abc_file: 340 self.passed = self.passed and abc_file.read() == self.compare_abc_str 341 os.remove(abc_path) 342 343 return self 344 345 346class Runner: 347 def __init__(self, args, name): 348 self.test_root = path.dirname(path.abspath(__file__)) 349 self.args = args 350 self.name = name 351 self.tests = [] 352 self.failed = 0 353 self.passed = 0 354 self.es2panda = path.join(args.build_dir, 'es2abc') 355 self.build_dir = args.build_dir 356 self.cmd_prefix = [] 357 self.ark_js_vm = "" 358 self.ark_aot_compiler = "" 359 self.ld_library_path = "" 360 361 if args.js_runtime_path: 362 self.ark_js_vm = path.join(args.js_runtime_path, 'ark_js_vm') 363 self.ark_aot_compiler = path.join(args.js_runtime_path, 'ark_aot_compiler') 364 365 if args.ld_library_path: 366 self.ld_library_path = args.ld_library_path 367 368 if args.arm64_qemu: 369 self.cmd_prefix = ["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu/"] 370 371 if args.arm32_qemu: 372 self.cmd_prefix = ["qemu-arm", "-L", "/usr/arm-linux-gnueabi"] 373 374 if not path.isfile(self.es2panda): 375 raise Exception("Cannot find es2panda binary: %s" % self.es2panda) 376 377 def add_directory(self, directory, extension, flags): 378 pass 379 380 def test_path(self, src): 381 pass 382 383 def run_test(self, test): 384 return test.run(self) 385 386 def run(self): 387 pool = multiprocessing.Pool() 388 result_iter = pool.imap_unordered( 389 self.run_test, self.tests, chunksize=32) 390 pool.close() 391 392 if self.args.progress: 393 from tqdm import tqdm 394 result_iter = tqdm(result_iter, total=len(self.tests)) 395 396 results = [] 397 for res in result_iter: 398 results.append(res) 399 400 self.tests = results 401 pool.join() 402 403 def deal_error(self, test): 404 path_str = test.path 405 err_col = {} 406 if test.error: 407 err_str = test.error.split('[')[0] if "patchfix" not in test.path else " patchfix throw error failed" 408 err_col = {"path" : [path_str], "status": ["fail"], "error" : [test.error], "type" : [err_str]} 409 else: 410 err_col = {"path" : [path_str], "status": ["fail"], "error" : ["Segmentation fault"], 411 "type" : ["Segmentation fault"]} 412 return err_col 413 414 def summarize(self): 415 print("") 416 fail_list = [] 417 success_list = [] 418 419 for test in self.tests: 420 assert(test.passed is not None) 421 if not test.passed: 422 fail_list.append(test) 423 else: 424 success_list.append(test) 425 426 if len(fail_list): 427 if self.args.error: 428 import pandas as pd 429 test_list = pd.DataFrame(columns=["path", "status", "error", "type"]) 430 for test in success_list: 431 suc_col = {"path" : [test.path], "status": ["success"], "error" : ["success"], "type" : ["success"]} 432 if self.args.error: 433 test_list = pd.concat([test_list, pd.DataFrame(suc_col)]) 434 print("Failed tests:") 435 for test in fail_list: 436 print(self.test_path(test.path)) 437 438 if self.args.error: 439 print("steps:", test.reproduce) 440 print("error:") 441 print(test.error) 442 print("\n") 443 err_col = self.deal_error(test) 444 test_list = pd.concat([test_list, pd.DataFrame(err_col)]) 445 446 if self.args.error: 447 test_list.to_csv('test_statistics.csv', index=False) 448 test_list["type"].value_counts().to_csv('type_statistics.csv', index_label="error") 449 print("Type statistics:\n", test_list["type"].value_counts()) 450 print("") 451 452 print("Summary(%s):" % self.name) 453 print("\033[37mTotal: %5d" % (len(self.tests))) 454 print("\033[92mPassed: %5d" % (len(self.tests) - len(fail_list))) 455 print("\033[91mFailed: %5d" % (len(fail_list))) 456 print("\033[0m") 457 458 return len(fail_list) 459 460 461class RegressionRunner(Runner): 462 def __init__(self, args): 463 Runner.__init__(self, args, "Regression") 464 465 def add_directory(self, directory, extension, flags, func=Test): 466 glob_expression = path.join( 467 self.test_root, directory, "*.%s" % (extension)) 468 files = glob(glob_expression) 469 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 470 471 self.tests += list(map(lambda f: func(f, flags), files)) 472 473 def test_path(self, src): 474 return src 475 476 477class AbcToAsmRunner(Runner): 478 def __init__(self, args, is_debug): 479 Runner.__init__(self, args, "Abc2asm" if not is_debug else "Abc2asmDebug") 480 self.is_debug = is_debug 481 482 def add_directory(self, directory, extension, flags, func=Test): 483 glob_expression = path.join( 484 self.test_root, directory, "*.%s" % (extension)) 485 files = glob(glob_expression) 486 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 487 488 self.tests += list(map(lambda f: AbcToAsmTest(f, flags, self.is_debug), files)) 489 490 def test_path(self, src): 491 return os.path.basename(src) 492 493 494class AbcToAsmTest(Test): 495 def __init__(self, test_path, flags, is_debug): 496 Test.__init__(self, test_path, flags) 497 self.is_debug = is_debug 498 499 def run(self, runner): 500 output_abc_file = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_") 501 # source code compilation, generate an abc file 502 gen_abc_cmd = runner.cmd_prefix + [runner.es2panda] 503 if (self.is_debug): 504 gen_abc_cmd.extend(["--debug-info"]) 505 gen_abc_cmd.extend(["--module", "--dump-normalized-asm-program", "--output=" + output_abc_file]) 506 gen_abc_cmd.append(self.path) 507 process_gen_abc = run_subprocess_with_beta3(self, gen_abc_cmd) 508 gen_abc_out, gen_abc_err = process_gen_abc.communicate() 509 gen_abc_output = gen_abc_out.decode("utf-8", errors="ignore") 510 511 # If no abc file is generated, an error occurs during parser, but abc2asm function is normal. 512 if not os.path.exists(output_abc_file): 513 self.passed = True 514 return self 515 516 # abc file compilation 517 abc_to_asm_cmd = runner.cmd_prefix + [runner.es2panda] 518 if (self.is_debug): 519 abc_to_asm_cmd.extend(["--debug-info"]) 520 abc_to_asm_cmd.extend(["--module", "--dump-normalized-asm-program", "--enable-abc-input"]) 521 abc_to_asm_cmd.append(output_abc_file) 522 process_abc_to_asm = run_subprocess_with_beta3(self, abc_to_asm_cmd) 523 abc_to_asm_out, abc_to_asm_err = process_abc_to_asm.communicate() 524 abc_to_asm_output = abc_to_asm_out.decode("utf-8", errors="ignore") 525 526 self.passed = gen_abc_output == abc_to_asm_output and process_abc_to_asm.returncode in [0, 1] 527 if not self.passed: 528 self.error = "Comparison of dump results between source code compilation and abc file compilation failed." 529 if gen_abc_err: 530 self.error += "\n" + gen_abc_err.decode("utf-8", errors="ignore") 531 if abc_to_asm_err: 532 self.error += "\n" + abc_to_asm_err.decode("utf-8", errors="ignore") 533 534 os.remove(output_abc_file) 535 return self 536 537 538class TSCRunner(Runner): 539 def __init__(self, args): 540 Runner.__init__(self, args, "TSC") 541 542 if self.args.tsc_path: 543 self.tsc_path = self.args.tsc_path 544 else : 545 self.tsc_path = prepare_tsc_testcases(self.test_root) 546 547 self.add_directory("conformance", []) 548 self.add_directory("compiler", []) 549 550 def add_directory(self, directory, flags): 551 ts_suite_dir = path.join(self.tsc_path, 'tests/cases') 552 553 glob_expression = path.join( 554 ts_suite_dir, directory, "**/*.ts") 555 files = glob(glob_expression, recursive=True) 556 files = fnmatch.filter(files, ts_suite_dir + '**' + self.args.filter) 557 558 for f in files: 559 test_name = path.basename(f.split(".ts")[0]) 560 negative_references = path.join( 561 self.tsc_path, 'tests/baselines/reference') 562 is_negative = path.isfile(path.join(negative_references, 563 test_name + ".errors.txt")) 564 test = TSCTest(f, flags) 565 566 if 'target' in test.options: 567 targets = test.options['target'].replace(" ", "").split(',') 568 for target in targets: 569 if path.isfile(path.join(negative_references, 570 test_name + "(target=%s).errors.txt" % (target))): 571 is_negative = True 572 break 573 574 if is_negative or "filename" in test.options: 575 continue 576 577 with open(path.join(self.test_root, 'test_tsc_ignore_list.txt'), 'r') as failed_references: 578 if self.args.skip: 579 if path.relpath(f, self.tsc_path) in failed_references.read(): 580 continue 581 582 self.tests.append(test) 583 584 def test_path(self, src): 585 return src 586 587 588class CompilerRunner(Runner): 589 def __init__(self, args): 590 Runner.__init__(self, args, "Compiler") 591 592 def add_directory(self, directory, extension, flags): 593 if directory.endswith("projects"): 594 projects_path = path.join(self.test_root, directory) 595 for project in os.listdir(projects_path): 596 glob_expression = path.join(projects_path, project, "**/*.%s" % (extension)) 597 files = glob(glob_expression, recursive=True) 598 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 599 self.tests.append(CompilerProjectTest(projects_path, project, files, flags)) 600 elif directory.endswith("protobin"): 601 test_path = path.join(self.test_root, directory) 602 for project in os.listdir(test_path): 603 self.tests.append(CompilerProtobinTest(path.join(test_path, project), flags)) 604 elif directory.endswith("abc2program"): 605 test_path = path.join(self.test_root, directory) 606 for project in os.listdir(test_path): 607 self.tests.append(CompilerAbcFileTest(path.join(test_path, project), flags)) 608 else: 609 glob_expression = path.join( 610 self.test_root, directory, "**/*.%s" % (extension)) 611 files = glob(glob_expression, recursive=True) 612 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 613 self.tests += list(map(lambda f: CompilerTest(f, flags), files)) 614 615 def test_path(self, src): 616 return src 617 618 619class CompilerTest(Test): 620 def __init__(self, test_path, flags): 621 Test.__init__(self, test_path, flags) 622 623 def execute_arkguard(self, runner): 624 input_file_path = self.path 625 arkguard_root_dir = os.path.join(runner.test_root, "../../arkguard") 626 arkgurad_entry_path = os.path.join(arkguard_root_dir, "lib/cli/SecHarmony.js") 627 config_path = os.path.join(arkguard_root_dir, "test/compilerTestConfig.json") 628 arkguard_cmd = [ 629 'node', 630 '--no-warnings', 631 arkgurad_entry_path, 632 input_file_path, 633 '--config-path', 634 config_path, 635 '--inplace' 636 ] 637 self.log_cmd(arkguard_cmd) 638 process = subprocess.Popen(arkguard_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 639 out, err = process.communicate() 640 process.wait() 641 success = True 642 if err or process.returncode != 0: 643 success = False 644 self.passed = False 645 self.error = err.decode("utf-8", errors="ignore") 646 return success 647 648 def run(self, runner): 649 test_abc_name = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_") 650 test_abc_path = path.join(runner.build_dir, test_abc_name) 651 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 652 es2abc_cmd.extend(self.flags) 653 es2abc_cmd.extend(["--output=" + test_abc_path]) 654 relative_path = os.path.relpath(self.path) 655 es2abc_cmd.append(relative_path) 656 enable_arkguard = runner.args.enable_arkguard 657 if enable_arkguard: 658 success = self.execute_arkguard(runner) 659 if not success: 660 return self 661 662 process = run_subprocess_with_beta3(self, es2abc_cmd) 663 out, err = process.communicate() 664 if "--dump-assembly" in self.flags: 665 pa_expected_path = "".join([self.get_path_to_expected()[:self.get_path_to_expected().rfind(".txt")], 666 ".pa.txt"]) 667 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 668 try: 669 with open(pa_expected_path, 'r') as fp: 670 expected = fp.read() 671 self.passed = expected == self.output and process.returncode in [0, 1] 672 except Exception: 673 self.passed = False 674 if not self.passed: 675 self.error = err.decode("utf-8", errors="ignore") 676 if os.path.exists(test_abc_path): 677 os.remove(test_abc_path) 678 return self 679 if "--dump-debug-info" in self.flags: 680 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 681 try: 682 with open(self.get_path_to_expected(), 'r') as fp: 683 expected = fp.read() 684 self.passed = expected == self.output and process.returncode in [0, 1] 685 if os.path.exists(test_abc_path): 686 os.remove(test_abc_path) 687 return self 688 except Exception: 689 self.passed = False 690 if not self.passed: 691 self.error = err.decode("utf-8", errors="ignore") 692 if os.path.exists(test_abc_path): 693 os.remove(test_abc_path) 694 return self 695 if err: 696 self.passed = False 697 self.error = err.decode("utf-8", errors="ignore") 698 return self 699 700 ld_library_path = runner.ld_library_path 701 os.environ.setdefault("LD_LIBRARY_PATH", ld_library_path) 702 run_abc_cmd = [runner.ark_js_vm, '--enable-force-gc=false', test_abc_path] 703 self.log_cmd(run_abc_cmd) 704 705 process = subprocess.Popen(run_abc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 706 out, err = process.communicate() 707 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 708 expected_path = self.get_path_to_expected() 709 try: 710 with open(expected_path, 'r') as fp: 711 expected = fp.read() 712 self.passed = expected == self.output and process.returncode in [0, 1] 713 except Exception: 714 self.passed = False 715 716 if not self.passed: 717 self.error = err.decode("utf-8", errors="ignore") 718 719 os.remove(test_abc_path) 720 721 return self 722 723 724class CompilerAbcFileTest(Test): 725 def __init__(self, test_dir, flags): 726 Test.__init__(self, test_dir, flags) 727 self.test_dir = test_dir 728 self.generated_path = os.path.join(self.test_dir, "gen") 729 if not path.exists(self.generated_path): 730 os.makedirs(self.generated_path) 731 self.original_abc_path = os.path.join(self.generated_path, "original.abc") 732 self.output_path = os.path.join(self.generated_path, "result.abc") 733 self.original_test = os.path.join(self.test_dir, "base.ts") 734 self.expected_path = os.path.join(self.test_dir, "expected.txt") 735 736 def remove_test_build(self, runner): 737 if path.exists(self.generated_path): 738 shutil.rmtree(self.generated_path) 739 740 def gen_abc(self, runner, test_path, output_path, flags): 741 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 742 es2abc_cmd.extend(['%s%s' % ("--output=", output_path)]) 743 es2abc_cmd.extend(flags) 744 es2abc_cmd.append(test_path) 745 process = run_subprocess_with_beta3(self, es2abc_cmd) 746 out, err = process.communicate() 747 if err: 748 self.passed = False 749 self.error = err.decode("utf-8", errors="ignore") 750 self.remove_test_build(runner) 751 return self 752 753 def run(self, runner): 754 new_flags = self.flags 755 # Generate 'abc' from the source file 756 self.gen_abc(runner, self.original_test, self.original_abc_path, new_flags) 757 # Generate 'abc' from the abc file 758 new_flags = self.flags 759 compile_context_info_path = path.join(self.test_dir, "compileContextInfo.json") 760 if path.exists(compile_context_info_path): 761 new_flags.append("%s%s" % ("--compile-context-info=", compile_context_info_path)) 762 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 763 es2abc_cmd.append('%s%s' % ("--output=", self.output_path)) 764 es2abc_cmd.append(self.original_abc_path) 765 es2abc_cmd.extend(new_flags) 766 process = run_subprocess_with_beta3(self, es2abc_cmd) 767 out, err = process.communicate() 768 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 769 try: 770 with open(self.expected_path, 'r') as fp: 771 expected = fp.read() 772 self.passed = expected == self.output and process.returncode in [0, 1] 773 except Exception: 774 self.passed = False 775 if not self.passed: 776 self.remove_test_build(runner) 777 return self 778 self.remove_test_build(runner) 779 return self 780 781 782class CompilerProtobinTest(Test): 783 def __init__(self, test_dir, flags): 784 Test.__init__(self, test_dir, flags) 785 self.test_dir = test_dir 786 self.generated_path = os.path.join(self.test_dir, "gen") 787 if not path.exists(self.generated_path): 788 os.makedirs(self.generated_path) 789 self.protobin_path = os.path.join(self.generated_path, "cache.protobin") 790 self.original_abc_path = os.path.join(self.generated_path, "base.abc") 791 self.output_path = os.path.join(self.generated_path, "module.abc") 792 self.original_test = os.path.join(self.test_dir, "base.ts") 793 self.modify_test = os.path.join(self.test_dir, "base_mod.ts") 794 self.expected_path = os.path.join(self.test_dir, "expected.txt") 795 796 def remove_test_build(self, runner): 797 if path.exists(self.generated_path): 798 shutil.rmtree(self.generated_path) 799 800 def gen_merge_abc(self, runner, test_path, need_cache, output_path): 801 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 802 es2abc_cmd.extend(["--merge-abc"]) 803 if need_cache: 804 es2abc_cmd.extend(["--enable-abc-input", '%s%s' % ("--cache-file=", self.protobin_path)]) 805 es2abc_cmd.extend(['%s%s' % ("--output=", output_path)]) 806 es2abc_cmd.append(test_path) 807 process = run_subprocess_with_beta3(self, es2abc_cmd) 808 out, err = process.communicate() 809 if err: 810 self.passed = False 811 self.error = err.decode("utf-8", errors="ignore") 812 self.remove_test_build(runner) 813 return self 814 815 def run(self, runner): 816 # Generate 'abc' from the source file before modifying it 817 self.gen_merge_abc(runner, self.original_test, False, self.original_abc_path) 818 # Generate protobin from the abc file before modifying it 819 self.gen_merge_abc(runner, self.original_abc_path, True, self.output_path) 820 # Modify the original abc file 821 self.gen_merge_abc(runner, self.modify_test, False, self.original_abc_path) 822 # Compile based on the modified abc file 823 self.gen_merge_abc(runner, self.original_abc_path, True, self.output_path) 824 ld_library_path = runner.ld_library_path 825 os.environ.setdefault("LD_LIBRARY_PATH", ld_library_path) 826 run_abc_cmd = [runner.ark_js_vm, '--entry-point=base', self.output_path] 827 self.log_cmd(run_abc_cmd) 828 829 process = subprocess.Popen(run_abc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 830 out, err = process.communicate() 831 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 832 try: 833 with open(self.expected_path, 'r') as fp: 834 expected = fp.read() 835 self.passed = expected == self.output and process.returncode in [0, 1] 836 except Exception: 837 self.passed = False 838 if not self.passed: 839 self.remove_test_build(runner) 840 return self 841 self.remove_test_build(runner) 842 return self 843 844 845class CompilerProjectTest(Test): 846 def __init__(self, projects_path, project, test_paths, flags): 847 Test.__init__(self, "", flags) 848 self.projects_path = projects_path 849 self.project = project 850 self.test_paths = test_paths 851 self.files_info_path = os.path.join(os.path.join(self.projects_path, self.project), 'filesInfo.txt') 852 self.files_info_mod_path = os.path.join(os.path.join(self.projects_path, self.project), 'filesInfoModify.txt') 853 # Skip execution if --dump-assembly exists in flags 854 self.requires_execution = "--dump-assembly" not in self.flags 855 self.file_record_mapping = None 856 self.generated_abc_inputs_path = os.path.join(os.path.join(self.projects_path, self.project), "abcinputs_gen") 857 self.abc_input_filenames = None 858 self.protoBin_file_path = "" 859 self.record_names_path = os.path.join(os.path.join(self.projects_path, self.project), 'recordnames.txt') 860 self.abc_inputs_path = os.path.join(os.path.join(self.projects_path, self.project), 'abcinputs') 861 # Modify the hap file path 862 self.project_mod_path = os.path.join(os.path.join(self.projects_path, self.project), 'mod') 863 self.modules_cache_path = os.path.join(os.path.join(self.projects_path, self.project), 'modulescache.cache') 864 self.deps_json_path = os.path.join(os.path.join(self.projects_path, self.project), 'deps-json.json') 865 # Merge hap need to modify package name 866 self.modifyPkgNamePath = os.path.join(os.path.join(self.projects_path, self.project), 'modify_pkg_name.txt') 867 868 def remove_project(self, runner): 869 project_path = runner.build_dir + "/" + self.project 870 if path.exists(project_path): 871 shutil.rmtree(project_path) 872 if path.exists(self.files_info_path): 873 os.remove(self.files_info_path) 874 if path.exists(self.files_info_mod_path): 875 os.remove(self.files_info_mod_path) 876 if path.exists(self.generated_abc_inputs_path): 877 shutil.rmtree(self.generated_abc_inputs_path) 878 if path.exists(self.protoBin_file_path): 879 os.remove(self.protoBin_file_path) 880 if path.exists(self.modules_cache_path): 881 self.remove_cache_files() 882 883 def remove_cache_files(self): 884 if path.exists(self.modules_cache_path): 885 with open(self.modules_cache_path) as cache_fp: 886 cache_lines = cache_fp.readlines() 887 for cache_line in cache_lines: 888 cache_file_path = cache_line[:-1].split(";")[1] 889 if path.exists(cache_file_path): 890 os.remove(cache_file_path) 891 os.remove(self.modules_cache_path) 892 893 def get_file_absolute_path_and_name(self, runner): 894 sub_path = self.path[len(self.projects_path):] 895 file_relative_path = path.split(sub_path)[0] 896 file_name = path.split(sub_path)[1] 897 file_absolute_path = runner.build_dir + "/" + file_relative_path 898 return [file_absolute_path, file_name] 899 900 def gen_single_abc(self, runner): 901 for test_path in self.test_paths: 902 self.path = test_path 903 [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner) 904 if not path.exists(file_absolute_path): 905 os.makedirs(file_absolute_path) 906 907 test_abc_name = ("%s.abc" % (path.splitext(file_name)[0])) 908 test_abc_path = path.join(file_absolute_path, test_abc_name) 909 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 910 es2abc_cmd.extend(self.flags) 911 es2abc_cmd.extend(['%s%s' % ("--output=", test_abc_path)]) 912 es2abc_cmd.append(self.path) 913 process = run_subprocess_with_beta3(self, es2abc_cmd) 914 out, err = process.communicate() 915 if err: 916 self.passed = False 917 self.error = err.decode("utf-8", errors="ignore") 918 self.remove_project(runner) 919 return self 920 921 def collect_record_mapping(self): 922 # Collect record mappings from recordnames.txt, file format: 923 # 'source_file_name:record_name\n' * n 924 if path.exists(self.record_names_path): 925 with open(self.record_names_path) as mapping_fp: 926 mapping_lines = mapping_fp.readlines() 927 self.file_record_mapping = {} 928 for mapping_line in mapping_lines: 929 cur_mapping = mapping_line[:-1].split(":") 930 self.file_record_mapping[cur_mapping[0]] = cur_mapping[1] 931 932 def get_record_name(self, test_path): 933 record_name = os.path.relpath(test_path, os.path.dirname(self.files_info_path)).split('.')[0] 934 if (self.file_record_mapping is not None and record_name in self.file_record_mapping): 935 record_name = self.file_record_mapping[record_name] 936 return record_name 937 938 def collect_abc_inputs(self, runner): 939 # Collect abc input information from the 'abcinputs' directory. Each txt file in the directory 940 # will generate a merged abc file with the same filename and serve as the final abc input. 941 # file format: 'source_file_name.ts\n' * n 942 if not path.exists(self.abc_inputs_path): 943 return 944 if not path.exists(self.generated_abc_inputs_path): 945 os.makedirs(self.generated_abc_inputs_path) 946 self.abc_input_filenames = {} 947 filenames = os.listdir(self.abc_inputs_path) 948 for filename in filenames: 949 if not filename.endswith('.txt'): 950 self.remove_project(runner) 951 raise Exception("Invalid abc input file: %s, only txt files are allowed in abcinputs directory: %s" 952 % (filename, self.abc_inputs_path)) 953 with open(path.join(self.abc_inputs_path, filename)) as abc_inputs_fp: 954 abc_inputs_lines = abc_inputs_fp.readlines() 955 for abc_input_line in abc_inputs_lines: 956 # filename is 'xxx.txt', remove '.txt' here 957 self.abc_input_filenames[abc_input_line[:-1]] = filename[:-len('.txt')] 958 959 def get_belonging_abc_input(self, test_path): 960 filename = os.path.relpath(test_path, os.path.dirname(self.files_info_path)) 961 if (self.abc_input_filenames is not None and filename in self.abc_input_filenames): 962 return self.abc_input_filenames[filename] 963 return None 964 965 def gen_abc_input_files_infos(self, runner, abc_files_infos, final_file_info_f, mod_files_info): 966 for abc_files_info_name in abc_files_infos: 967 abc_files_info = abc_files_infos[abc_files_info_name] 968 if len(abc_files_info) != 0: 969 abc_input_path = path.join(self.generated_abc_inputs_path, abc_files_info_name) 970 abc_files_info_path = ("%s-filesInfo.txt" % (abc_input_path)) 971 abc_files_info_fd = os.open(abc_files_info_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC) 972 abc_files_info_f = os.fdopen(abc_files_info_fd, 'w') 973 abc_files_info_f.writelines(abc_files_info) 974 abc_line = '%s-abcinput.abc;;;;%s;\n' % (abc_input_path, abc_files_info_name) 975 mod_files_info.append(abc_line) 976 final_file_info_f.writelines(abc_line) 977 978 def gen_files_info(self, runner): 979 # After collect_record_mapping, self.file_record_mapping stores {'source file name' : 'source file record name'} 980 self.collect_record_mapping() 981 # After collect_abc_inputs, self.abc_input_filenames stores {'source file name' : 'belonging abc input name'} 982 self.collect_abc_inputs(runner) 983 984 fd = os.open(self.files_info_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC) 985 f = os.fdopen(fd, 'w') 986 mod_files_info = [] 987 abc_files_infos = {} 988 for test_path in self.test_paths: 989 record_name = self.get_record_name(test_path) 990 module_kind = 'esm' 991 if (os.path.basename(test_path).startswith("commonjs")): 992 module_kind = 'commonjs' 993 is_shared_module = 'false' 994 if (os.path.basename(test_path).startswith("sharedmodule")): 995 is_shared_module = 'true' 996 file_info = ('%s;%s;%s;%s;%s;%s\n' % (test_path, record_name, module_kind, 997 os.path.relpath(test_path, self.projects_path), record_name, 998 is_shared_module)) 999 belonging_abc_input = self.get_belonging_abc_input(test_path) 1000 if belonging_abc_input is not None: 1001 if not belonging_abc_input in abc_files_infos: 1002 abc_files_infos[belonging_abc_input] = [] 1003 abc_files_infos[belonging_abc_input].append(file_info) 1004 elif test_path.startswith(self.project_mod_path): 1005 mod_files_info.append(file_info) 1006 else: 1007 mod_files_info.append(file_info) 1008 f.writelines(file_info) 1009 if (os.path.exists(self.deps_json_path)): 1010 record_name = self.get_record_name(self.deps_json_path) 1011 file_info = ('%s;%s;%s;%s;%s;%s\n' % (self.deps_json_path, record_name, 'esm', 1012 os.path.relpath(self.deps_json_path, self.projects_path), record_name, 1013 'false')) 1014 f.writelines(file_info) 1015 self.gen_abc_input_files_infos(runner, abc_files_infos, f, mod_files_info) 1016 f.close() 1017 if (os.path.exists(self.project_mod_path)): 1018 mod_fd = os.open(self.files_info_mod_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC) 1019 mod_f = os.fdopen(mod_fd, 'w') 1020 for file_line in mod_files_info: 1021 mod_f.writelines(file_line) 1022 mod_f.close() 1023 1024 def gen_modules_cache(self, runner): 1025 if "--cache-file" not in self.flags or "--file-threads=0" in self.flags: 1026 return 1027 fd = os.open(self.modules_cache_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC) 1028 f = os.fdopen(fd, 'w') 1029 abc_files = set() 1030 for test_path in self.test_paths: 1031 cache_info = ('%s;%s\n' % (test_path, f"{test_path.rsplit('.', 1)[0]}.protobin")) 1032 belonging_abc_input = self.get_belonging_abc_input(test_path) 1033 if belonging_abc_input is not None: 1034 abc_files.add(belonging_abc_input) 1035 else: 1036 f.writelines(cache_info) 1037 for abc_path in abc_files: 1038 abc_input_path = f"{path.join(self.generated_abc_inputs_path, abc_path)}-abcinput.abc" 1039 cache_info = ('%s;%s\n' % (abc_input_path, f"{abc_input_path.rsplit('.', 1)[0]}.protobin")) 1040 f.writelines(cache_info) 1041 f.close() 1042 1043 def gen_es2abc_cmd(self, runner, input_file, output_file): 1044 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 1045 1046 new_flags = self.flags 1047 if "--cache-file" in new_flags and len(self.test_paths) == 1: 1048 # Generate cache-file test case in single thread 1049 new_flags.remove("--cache-file") 1050 protobin_path = f"{self.test_paths[0].rsplit('.', 1)[0]}.protobin" 1051 self.protoBin_file_path = protobin_path 1052 es2abc_cmd.append('--cache-file=%s' % (protobin_path)) 1053 elif "--cache-file" in self.flags and output_file.endswith("-abcinput.abc"): 1054 # Generate abc for bytecode har 1055 new_flags = list(filter(lambda x: x != "--cache-file", new_flags)) 1056 elif "--cache-file" in self.flags: 1057 new_flags = list(filter(lambda x: x != "--cache-file", new_flags)) 1058 es2abc_cmd.append('--cache-file') 1059 es2abc_cmd.append('@%s' % (self.modules_cache_path)) 1060 1061 es2abc_cmd.extend(new_flags) 1062 es2abc_cmd.extend(['%s%s' % ("--output=", output_file)]) 1063 es2abc_cmd.append(input_file) 1064 return es2abc_cmd 1065 1066 def gen_merged_abc_for_abc_input(self, runner, files_info_name): 1067 self.passed = True 1068 if not files_info_name.endswith(".txt"): 1069 return 1070 abc_input_files_info_path = path.join(self.generated_abc_inputs_path, files_info_name) 1071 abc_input_merged_abc_path = path.join(self.generated_abc_inputs_path, 1072 '%s-abcinput.abc' % (files_info_name[:-len('-filesInfo.txt')])) 1073 1074 abc_input_file_path = '@' + abc_input_files_info_path 1075 if "unmerged_abc_input" in self.generated_abc_inputs_path: 1076 self.flags.remove("--merge-abc") 1077 with open(abc_input_files_info_path, 'r') as fp: 1078 abc_input_file_path = fp.read().split(';')[0] 1079 1080 es2abc_cmd = self.gen_es2abc_cmd(runner, abc_input_file_path, abc_input_merged_abc_path) 1081 process = run_subprocess_with_beta3(self, es2abc_cmd) 1082 out, err = process.communicate() 1083 if err: 1084 self.passed = False 1085 self.error = err.decode("utf-8", errors="ignore") 1086 1087 def gen_merged_abc(self, runner): 1088 output_abc_name = "" 1089 exec_file_path = "" 1090 # Generate abc inputs 1091 if (os.path.exists(self.generated_abc_inputs_path)): 1092 files_info_names = os.listdir(self.generated_abc_inputs_path) 1093 for filename in files_info_names: 1094 self.gen_merged_abc_for_abc_input(runner, filename) 1095 if (not self.passed): 1096 self.remove_project(runner) 1097 return self 1098 # Generate the abc to be tested 1099 for test_path in self.test_paths: 1100 self.path = test_path 1101 if (self.path.endswith("-exec.ts")) or (self.path.endswith("-exec.js")): 1102 exec_file_path = self.path 1103 [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner) 1104 if not path.exists(file_absolute_path): 1105 os.makedirs(file_absolute_path) 1106 test_abc_name = ("%s.abc" % (path.splitext(file_name)[0])) 1107 output_abc_name = path.join(file_absolute_path, test_abc_name) 1108 if "merge_hap" in self.projects_path: 1109 exec_file_path = os.path.join(self.projects_path, self.project) 1110 exec_file_path = os.path.join(exec_file_path, "main_hap") 1111 [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner) 1112 if not path.exists(file_absolute_path): 1113 os.makedirs(file_absolute_path) 1114 output_abc_name = path.join(file_absolute_path, "merge_hap.abc") 1115 # reverse merge-abc flag 1116 if "merge_abc_consistence_check" in self.path: 1117 if "--merge-abc" in self.flags: 1118 self.flags.remove("--merge-abc") 1119 else: 1120 self.flags.append("--merge-abc") 1121 1122 es2abc_cmd = self.gen_es2abc_cmd(runner, '@' + self.files_info_path, output_abc_name) 1123 if "--cache-file" in self.flags and len(self.test_paths) == 1: 1124 es2abc_cmd = self.gen_es2abc_cmd(runner, self.test_paths[0], output_abc_name) 1125 else: 1126 es2abc_cmd = self.gen_es2abc_cmd(runner, '@' + self.files_info_path, output_abc_name) 1127 compile_context_info_path = path.join(path.join(self.projects_path, self.project), "compileContextInfo.json") 1128 if path.exists(compile_context_info_path): 1129 es2abc_cmd.append("%s%s" % ("--compile-context-info=", compile_context_info_path)) 1130 if path.exists(self.modifyPkgNamePath): 1131 with open(self.modifyPkgNamePath, 'r') as file: 1132 modifyPkgName = file.readline().rstrip('\n') 1133 pkgNames = modifyPkgName.split(":") 1134 es2abc_cmd.append("--src-package-name=%s" % pkgNames[0]) 1135 es2abc_cmd.append("--dst-package-name=%s" % pkgNames[1]) 1136 process = run_subprocess_with_beta3(self, es2abc_cmd) 1137 self.path = exec_file_path 1138 out, err = [None, None] 1139 # Check single-thread execution timeout when required 1140 if "--file-threads=0" in self.flags: 1141 try: 1142 out, err = process.communicate(timeout=60) 1143 except: 1144 process.kill() 1145 print("Generating the abc file timed out.") 1146 else: 1147 out, err = process.communicate() 1148 1149 if "non-merged-abc" in self.path and "--merge-abc" in es2abc_cmd: 1150 es2abc_cmd.remove("--merge-abc") 1151 1152 if "--cache-file" in self.flags: 1153 # Firstly generate cache file, and generate abc from cache file 1154 if (os.path.exists(self.project_mod_path)): 1155 es2abc_cmd = self.gen_es2abc_cmd(runner, '@' + self.files_info_mod_path, output_abc_name) 1156 compile_context_info_path = path.join(path.join(self.projects_path, self.project), "compileContextInfo.json") 1157 if path.exists(compile_context_info_path): 1158 es2abc_cmd.append("%s%s" % ("--compile-context-info=", compile_context_info_path)) 1159 process = run_subprocess_with_beta3(self, es2abc_cmd) 1160 out, err = process.communicate() 1161 else: 1162 process = run_subprocess_with_beta3(self, es2abc_cmd) 1163 out, err = process.communicate() 1164 1165 # restore merge-abc flag 1166 if "merge_abc_consistence_check" in self.path and "--merge-abc" not in self.flags: 1167 self.flags.append("--merge-abc") 1168 1169 # Check dump-assembly outputs when required 1170 if "--dump-assembly" in self.flags: 1171 pa_expected_path = "".join([self.get_path_to_expected()[:self.get_path_to_expected().rfind(".txt")], 1172 ".pa.txt"]) 1173 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 1174 if "merge_abc_consistence_check" in self.path: 1175 self.output = self.output.split('.')[0] 1176 try: 1177 with open(pa_expected_path, 'r') as fp: 1178 expected = fp.read() 1179 self.passed = expected == self.output and process.returncode in [0, 1] 1180 except Exception: 1181 self.passed = False 1182 if not self.passed: 1183 self.error = err.decode("utf-8", errors="ignore") 1184 self.remove_project(runner) 1185 return self 1186 else: 1187 return self 1188 1189 if err: 1190 self.passed = False 1191 self.error = err.decode("utf-8", errors="ignore") 1192 self.remove_project(runner) 1193 return self 1194 1195 def run(self, runner): 1196 # Compile all ts source files in the project to abc files. 1197 if ("--merge-abc" in self.flags): 1198 self.gen_files_info(runner) 1199 self.gen_modules_cache(runner) 1200 self.gen_merged_abc(runner) 1201 else: 1202 self.gen_single_abc(runner) 1203 1204 if (not self.requires_execution): 1205 self.remove_project(runner) 1206 return self 1207 1208 # Run test files that need to be executed in the project. 1209 for test_path in self.test_paths: 1210 self.path = test_path 1211 if self.path.endswith("-exec.ts"): 1212 [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner) 1213 1214 entry_point_name = path.splitext(file_name)[0] 1215 test_abc_name = ("%s.abc" % entry_point_name) 1216 test_abc_path = path.join(file_absolute_path, test_abc_name) 1217 1218 ld_library_path = runner.ld_library_path 1219 os.environ.setdefault("LD_LIBRARY_PATH", ld_library_path) 1220 run_abc_cmd = [runner.ark_js_vm] 1221 if ("--merge-abc" in self.flags): 1222 run_abc_cmd.extend(["--entry-point", entry_point_name]) 1223 run_abc_cmd.extend([test_abc_path]) 1224 self.log_cmd(run_abc_cmd) 1225 1226 process = subprocess.Popen(run_abc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1227 out, err = process.communicate() 1228 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 1229 expected_path = self.get_path_to_expected() 1230 try: 1231 with open(expected_path, 'r') as fp: 1232 expected = fp.read() 1233 self.passed = expected == self.output and process.returncode in [0, 1] 1234 except Exception: 1235 self.passed = False 1236 1237 if not self.passed: 1238 self.error = err.decode("utf-8", errors="ignore") 1239 self.remove_project(runner) 1240 return self 1241 1242 self.passed = True 1243 1244 self.remove_project(runner) 1245 return self 1246 1247 1248class FilesInfoRunner(Runner): 1249 def __init__(self, args): 1250 Runner.__init__(self, args, "FilesInfo") 1251 1252 def add_directory(self, directory, extension, flags): 1253 projects_path = path.join(self.test_root, directory) 1254 test_projects = ["base", "mod"] 1255 for project in test_projects: 1256 project_dir = path.join(projects_path, project) 1257 if not path.isdir(project_dir): 1258 continue 1259 filesinfo_path = path.join(project_dir, "filesInfo.txt") 1260 self.tests.append(FilesInfoTest(projects_path, project, filesinfo_path, flags)) 1261 1262 def test_path(self, src): 1263 return src 1264 1265 1266class FilesInfoTest(Test): 1267 LONG_PATH_MARKER = "long_path_filesinfo" 1268 1269 def __init__(self, projects_path, project, filesinfo_path, flags): 1270 Test.__init__(self, "", flags) 1271 self.projects_path = projects_path 1272 self.output_path = path.join(projects_path, "output") 1273 self.project = project 1274 self.origin_filesinfo_path = filesinfo_path 1275 self.files_info_path = path.join(self.output_path, self.project + "filesInfo.txt") 1276 self.path = path.join(self.projects_path, self.project) 1277 self.symbol_table_file = os.path.join(self.output_path, 'base.map') 1278 self.output_abc_name = path.join(self.output_path, self.project + ".abc") 1279 self.output_abc_name_of_input_abc = path.join(self.output_path, self.project + "_input.abc") 1280 self.is_long_path_case = self.LONG_PATH_MARKER in self.path 1281 1282 if not path.exists(self.output_path): 1283 os.makedirs(self.output_path) 1284 1285 def gen_files_info(self): 1286 with open(self.origin_filesinfo_path, 'r') as src, open(self.files_info_path, 'w') as dst: 1287 for line in src: 1288 dst.write(f"{path.join(self.projects_path, self.project)}/{line}") 1289 1290 def remove_output(self): 1291 shutil.rmtree(self.output_path) 1292 1293 def remove_project(self, runner): 1294 if self.project == "mod": # clear after all tests 1295 self.remove_output() 1296 1297 def gen_es2abc_cmd(self, runner, input_file, output_file): 1298 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 1299 es2abc_cmd.extend(self.flags) 1300 es2abc_cmd.extend(['%s%s' % ("--output=", output_file)]) 1301 es2abc_cmd.append(input_file) 1302 if ("base" == self.project): 1303 es2abc_cmd.extend(['--dump-symbol-table', self.symbol_table_file]) 1304 else: 1305 es2abc_cmd.extend(['--input-symbol-table', self.symbol_table_file]) 1306 return es2abc_cmd 1307 1308 def gen_es2abc_cmd_input_abc(self, runner, input_file, output_file): 1309 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 1310 es2abc_cmd.extend(self.flags) 1311 es2abc_cmd.extend(['%s%s' % ("--output=", output_file), "--enable-abc-input"]) 1312 es2abc_cmd.append(input_file) 1313 return es2abc_cmd 1314 1315 def get_long_path_filesinfo_path(self): 1316 long_dir = "a" * 4096 1317 return path.join(self.projects_path, self.project, long_dir, "filesInfo.txt") 1318 1319 def replace_expected_file_root(self, expected): 1320 full_root_path = os.path.abspath(path.join(self.projects_path, self.project)) 1321 return expected.replace("{{EXPECTED_FILE_ROOT}}", full_root_path) 1322 1323 def gen_merged_abc(self, runner): 1324 # Generate long path filesinfo if it is path contains long path 1325 if self.is_long_path_case: 1326 self.files_info_path = self.get_long_path_filesinfo_path() 1327 1328 # Generate the abc to be tested 1329 es2abc_cmd = self.gen_es2abc_cmd(runner, '@' + self.files_info_path, self.output_abc_name) 1330 process = run_subprocess_with_beta3(self, es2abc_cmd) 1331 out, err = process.communicate() 1332 1333 # Gen abc and verify it 1334 pa_expected_path = "".join([self.get_path_to_expected()[:self.get_path_to_expected().rfind(".txt")], 1335 ".pa.txt"]) 1336 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 1337 try: 1338 with open(pa_expected_path, 'r') as fp: 1339 expected = fp.read() 1340 if self.is_long_path_case: 1341 expected = self.replace_expected_file_root(expected) 1342 self.passed = expected == self.output and process.returncode in [0, 1] 1343 except Exception: 1344 self.passed = False 1345 if not self.passed: 1346 self.error = err.decode("utf-8", errors="ignore") 1347 return self 1348 1349 # Input abc and verify it when it is base. 1350 if self.project == "base": 1351 self.input_abc(runner) 1352 return self 1353 1354 def input_abc(self, runner): 1355 es2abc_cmd = self.gen_es2abc_cmd_input_abc(runner, self.output_abc_name, self.output_abc_name_of_input_abc) 1356 process = run_subprocess_with_beta3(self, es2abc_cmd) 1357 out, err = process.communicate() 1358 pa_expected_path = "".join([self.path, "input_base-expected.pa.txt"]) 1359 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 1360 try: 1361 with open(pa_expected_path, 'r') as fp: 1362 expected = fp.read() 1363 self.passed = expected == self.output and process.returncode in [0, 1] 1364 except Exception: 1365 self.passed = False 1366 if not self.passed: 1367 self.error = err.decode("utf-8", errors="ignore") 1368 return self 1369 1370 1371 def run(self, runner): 1372 self.gen_files_info() 1373 self.gen_merged_abc(runner) 1374 self.remove_project(runner) 1375 return self 1376 1377 1378class TSDeclarationTest(Test): 1379 def get_path_to_expected(self): 1380 file_name = self.path[:self.path.find(".d.ts")] 1381 return "%s-expected.txt" % file_name 1382 1383 1384class BcVersionRunner(Runner): 1385 def __init__(self, args): 1386 Runner.__init__(self, args, "Target bc version") 1387 self.ts2abc = path.join(self.test_root, '..', 'scripts', 'ts2abc.js') 1388 1389 def add_cmd(self): 1390 api_sub_version_list = ["beta1", "beta2", "beta3"] 1391 for api_version in range(8, 17): 1392 cmd = self.cmd_prefix + [self.es2panda] 1393 cmd += ["--target-bc-version"] 1394 cmd += ["--target-api-version"] 1395 cmd += [str(api_version)] 1396 self.tests += [BcVersionTest(cmd, api_version)] 1397 node_cmd = ["node"] + [self.ts2abc] 1398 node_cmd += ["".join(["es2abc=", self.es2panda])] 1399 node_cmd += ["--target-api-version"] 1400 node_cmd += [str(api_version)] 1401 self.tests += [BcVersionTest(node_cmd, api_version)] 1402 1403 # Add tests for "--target-api-sub-version" option 1404 if api_version == 12: 1405 for api_sub_version in api_sub_version_list: 1406 new_cmd = cmd.copy() 1407 new_cmd += ["--target-api-sub-version", api_sub_version] 1408 self.tests += [BcVersionTest(new_cmd, str(api_version) + '_' + api_sub_version)] 1409 new_node_cmd = node_cmd.copy() 1410 new_node_cmd += ["--target-api-sub-version", api_sub_version] 1411 self.tests += [BcVersionTest(new_node_cmd, str(api_version) + '_' + api_sub_version)] 1412 1413 def run(self): 1414 for test in self.tests: 1415 test.run() 1416 1417 1418class BcVersionTest(Test): 1419 def __init__(self, cmd, api_version): 1420 Test.__init__(self, "", 0) 1421 self.cmd = cmd 1422 self.api_version = api_version 1423 # To avoid problems when api version is upgraded abruptly, 1424 # the corresponding bytecode version of the api version not written in isa.yaml is alaways the newest version. 1425 self.bc_version_expect = { 1426 8: "13.0.1.0", 1427 9: "9.0.0.0", 1428 10: "9.0.0.0", 1429 11: "11.0.2.0", 1430 12: "12.0.2.0", 1431 "12_beta1": "12.0.2.0", 1432 "12_beta2": "12.0.2.0", 1433 "12_beta3": "12.0.6.0", 1434 13: "12.0.6.0", 1435 14: "12.0.6.0", 1436 15: "12.0.6.0", 1437 16: "12.0.6.0", 1438 17: "12.0.6.0", 1439 18: "13.0.1.0", 1440 19: "13.0.1.0", 1441 20: "13.0.1.0", 1442 } 1443 self.es2abc_script_expect = { 1444 8: "0.0.0.2", 1445 9: "9.0.0.0", 1446 10: "9.0.0.0", 1447 11: "11.0.2.0", 1448 12: "12.0.2.0", 1449 "12_beta1": "12.0.2.0", 1450 "12_beta2": "12.0.2.0", 1451 "12_beta3": "12.0.6.0", 1452 13: "12.0.6.0", 1453 14: "12.0.6.0", 1454 15: "12.0.6.0", 1455 16: "12.0.6.0", 1456 17: "12.0.6.0", 1457 18: "13.0.1.0", 1458 19: "13.0.1.0", 1459 20: "13.0.1.0", 1460 } 1461 1462 def run(self): 1463 process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1464 out, err = process.communicate() 1465 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 1466 if self.cmd[0] == "node": 1467 self.passed = self.es2abc_script_expect.get(self.api_version) == self.output and process.returncode in [0, 1] 1468 else: 1469 self.passed = self.bc_version_expect.get(self.api_version) == self.output and process.returncode in [0, 1] 1470 if not self.passed: 1471 self.error = err.decode("utf-8", errors="ignore") 1472 return self 1473 1474 1475class TransformerRunner(Runner): 1476 def __init__(self, args): 1477 Runner.__init__(self, args, "Transformer") 1478 1479 def add_directory(self, directory, extension, flags): 1480 glob_expression = path.join( 1481 self.test_root, directory, "**/*.%s" % (extension)) 1482 files = glob(glob_expression, recursive=True) 1483 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 1484 1485 self.tests += list(map(lambda f: TransformerTest(f, flags), files)) 1486 1487 def test_path(self, src): 1488 return src 1489 1490 1491class TransformerInTargetApiVersion10Runner(Runner): 1492 def __init__(self, args): 1493 Runner.__init__(self, args, "TransformerInTargetApiVersion10") 1494 1495 def add_directory(self, directory, extension, flags): 1496 glob_expression = path.join( 1497 self.test_root, directory, "**/*.%s" % (extension)) 1498 files = glob(glob_expression, recursive=True) 1499 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 1500 1501 self.tests += list(map(lambda f: TransformerTest(f, flags), files)) 1502 1503 def test_path(self, src): 1504 return src 1505 1506 1507class TransformerTest(Test): 1508 def __init__(self, test_path, flags): 1509 Test.__init__(self, test_path, flags) 1510 1511 def get_path_to_expected(self): 1512 return "%s-transformed-expected.txt" % (path.splitext(self.path)[0]) 1513 1514 def run(self, runner): 1515 cmd = runner.cmd_prefix + [runner.es2panda] 1516 cmd.extend(self.flags) 1517 cmd.append(self.path) 1518 process = run_subprocess_with_beta3(self, cmd) 1519 out, err = process.communicate() 1520 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 1521 1522 expected_path = self.get_path_to_expected() 1523 try: 1524 with open(expected_path, 'r') as fp: 1525 expected = fp.read() 1526 self.passed = expected == self.output and process.returncode in [0, 1] 1527 except Exception: 1528 self.passed = False 1529 1530 if not self.passed: 1531 self.error = err.decode("utf-8", errors="ignore") 1532 1533 return self 1534 1535 1536class PatchTest(Test): 1537 def __init__(self, test_path, mode_arg, target_version, preserve_files): 1538 Test.__init__(self, test_path, "") 1539 self.mode = mode_arg 1540 self.target_version = target_version 1541 self.preserve_files = preserve_files 1542 1543 def is_ts_test_cases(self): 1544 return 'ts_test_cases' in os.path.normpath(self.path).split(os.sep) 1545 1546 def need_to_merge(self): 1547 # If the test case is in the 'export and import' directory, it needs to be merged. 1548 if os.path.split(self.path)[-2] == 'export-and-import': 1549 return True 1550 else: 1551 return False 1552 1553 def gen_files_infos(self, modified=False): 1554 if not self.need_to_merge(): 1555 return 1556 1557 file_suffix = '.ts' if self.is_ts_test_cases() else '.js' 1558 file_name_list = [] 1559 for file_name in os.listdir(self.path): 1560 if file_name.endswith(file_suffix): 1561 if '_mod' in file_name and modified: 1562 file_name_list.append(file_name) 1563 elif not '_mod' in file_name and not modified: 1564 file_name_list.append(file_name) 1565 1566 files_info_txt = os.path.join(self.path, 'filesInfo.txt') 1567 with open(files_info_txt, 'w', encoding='utf-8') as file: 1568 for file_name in file_name_list: 1569 file_path = os.path.join(self.path, file_name) 1570 file_prev, file_extension = os.path.splitext(file_name) 1571 abc_file_path = os.path.join(self.path, f"{file_prev}.abc") 1572 file.write(f'{file_path};{file_name};{file_name};esm;{file_name};false\n') 1573 1574 def gen_cmd(self, runner): 1575 symbol_table_file = os.path.join(self.path, 'base.map') 1576 origin_input_file = 'base.js' if not self.is_ts_test_cases() else 'base.ts' 1577 origin_output_abc = os.path.join(self.path, 'base.abc') 1578 modified_input_file = 'base_mod.js' if not self.is_ts_test_cases() else 'base_mod.ts' 1579 modified_output_abc = os.path.join(self.path, 'patch.abc') 1580 files_info_txt = os.path.join(self.path, 'filesInfo.txt') 1581 target_version_cmd = "" 1582 if self.target_version > 0: 1583 target_version_cmd = "--target-api-version=" + str(self.target_version) 1584 1585 gen_base_cmd = runner.cmd_prefix + [runner.es2panda, '--module', target_version_cmd] 1586 if self.need_to_merge(): 1587 gen_base_cmd = gen_base_cmd.append('--merge-abc') 1588 if 'record-name-with-dots' in os.path.basename(self.path): 1589 gen_base_cmd.extend(['--merge-abc', '--record-name=record.name.with.dots']) 1590 gen_base_cmd.extend(['--dump-symbol-table', symbol_table_file]) 1591 gen_base_cmd.extend(['--output', origin_output_abc]) 1592 if not self.need_to_merge(): 1593 gen_base_cmd.extend([os.path.join(self.path, origin_input_file)]) 1594 else: 1595 gen_base_cmd.extend(['--debug-info', f'@{files_info_txt}']) 1596 self.log_cmd(gen_base_cmd) 1597 1598 if self.mode == 'hotfix': 1599 mode_arg = ["--generate-patch"] 1600 elif self.mode == 'hotreload': 1601 mode_arg = ["--hot-reload"] 1602 elif self.mode == 'coldfix': 1603 mode_arg = ["--generate-patch", "--cold-fix"] 1604 elif self.mode == 'coldreload': 1605 mode_arg = ["--cold-reload"] 1606 1607 patch_test_cmd = runner.cmd_prefix + [runner.es2panda, '--module', target_version_cmd] 1608 if self.need_to_merge(): 1609 patch_test_cmd = patch_test_cmd.append('--merge-abc') 1610 patch_test_cmd.extend(mode_arg) 1611 patch_test_cmd.extend(['--input-symbol-table', symbol_table_file]) 1612 patch_test_cmd.extend(['--output', modified_output_abc]) 1613 if not self.need_to_merge(): 1614 patch_test_cmd.extend([os.path.join(self.path, modified_input_file)]) 1615 else: 1616 patch_test_cmd.extend(['--debug-info', f'@{files_info_txt}']) 1617 if 'record-name-with-dots' in os.path.basename(self.path): 1618 patch_test_cmd.extend(['--merge-abc', '--record-name=record.name.with.dots']) 1619 dump_assembly_testname = [ 1620 'modify-anon-content-keep-origin-name', 1621 'modify-class-memeber-function', 1622 'exist-lexenv-3', 1623 'lexenv-reduce', 1624 'lexenv-increase'] 1625 for name in dump_assembly_testname: 1626 if name in os.path.basename(self.path): 1627 patch_test_cmd.extend(['--dump-assembly']) 1628 self.log_cmd(patch_test_cmd) 1629 1630 return gen_base_cmd, patch_test_cmd, symbol_table_file, origin_output_abc, modified_output_abc 1631 1632 def run(self, runner): 1633 gen_base_cmd, patch_test_cmd, symbol_table_file, origin_output_abc, modified_output_abc = self.gen_cmd(runner) 1634 1635 self.gen_files_infos(False) 1636 process_base = run_subprocess_with_beta3(None, gen_base_cmd) 1637 stdout_base, stderr_base = process_base.communicate(timeout=runner.args.es2panda_timeout) 1638 if stderr_base: 1639 self.passed = False 1640 self.error = stderr_base.decode("utf-8", errors="ignore") 1641 self.output = stdout_base.decode("utf-8", errors="ignore") + stderr_base.decode("utf-8", errors="ignore") 1642 else: 1643 self.gen_files_infos(True) 1644 process_patch = run_subprocess_with_beta3(None, patch_test_cmd) 1645 process_patch = subprocess.Popen(patch_test_cmd, stdout=subprocess.PIPE, 1646 stderr=subprocess.PIPE) 1647 stdout_patch, stderr_patch = process_patch.communicate(timeout=runner.args.es2panda_timeout) 1648 if stderr_patch: 1649 self.passed = False 1650 self.error = stderr_patch.decode("utf-8", errors="ignore") 1651 self.output = stdout_patch.decode("utf-8", errors="ignore") + stderr_patch.decode("utf-8", errors="ignore") 1652 1653 expected_path = os.path.join(self.path, 'expected.txt') 1654 try: 1655 with open(expected_path, 'r') as fp: 1656 # ignore license description lines and skip leading blank lines 1657 expected = (''.join((fp.readlines()[12:]))).lstrip() 1658 self.passed = expected == self.output 1659 except Exception: 1660 self.passed = False 1661 1662 if not self.passed: 1663 self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\ 1664 self.output 1665 files_info_txt = os.path.join(self.path, 'filesInfo.txt') 1666 if not self.preserve_files: 1667 os.remove(symbol_table_file) 1668 os.remove(origin_output_abc) 1669 if (os.path.exists(modified_output_abc)): 1670 os.remove(modified_output_abc) 1671 if (os.path.exists(files_info_txt)): 1672 os.remove(files_info_txt) 1673 return self 1674 1675 1676class PatchRunner(Runner): 1677 def __init__(self, args, name): 1678 Runner.__init__(self, args, name) 1679 self.preserve_files = args.error 1680 self.tests_in_dirs = [] 1681 dirs = os.listdir(path.join(self.test_root, "patch")) 1682 for target_version_path in dirs: 1683 self.add_tests(target_version_path, name) 1684 1685 def add_tests(self, target_version_path, name): 1686 name_dir = os.path.join(self.test_root, "patch", target_version_path, name) 1687 if not os.path.exists(name_dir): 1688 return 1689 target_version = 0 1690 if target_version_path.isdigit(): 1691 target_version = int(target_version_path) 1692 for sub_path in os.listdir(name_dir): 1693 test_base_path = os.path.join(name_dir, sub_path) 1694 if name != "coldreload": 1695 for dirpath, dirnames, filenames in os.walk(test_base_path): 1696 if not dirnames: 1697 self.tests_in_dirs.append(dirpath) 1698 self.tests.append(PatchTest(dirpath, name, target_version, self.preserve_files)) 1699 else: 1700 self.tests_in_dirs.append(test_base_path) 1701 self.tests.append(PatchTest(test_base_path, name, target_version, self.preserve_files)) 1702 1703 def test_path(self, src): 1704 return os.path.basename(src) 1705 1706 1707class HotfixRunner(PatchRunner): 1708 def __init__(self, args): 1709 PatchRunner.__init__(self, args, "hotfix") 1710 1711 1712class HotreloadRunner(PatchRunner): 1713 def __init__(self, args): 1714 PatchRunner.__init__(self, args, "hotreload") 1715 1716 1717class ColdfixRunner(PatchRunner): 1718 def __init__(self, args): 1719 PatchRunner.__init__(self, args, "coldfix") 1720 1721 1722class ColdreloadRunner(PatchRunner): 1723 def __init__(self, args): 1724 PatchRunner.__init__(self, args, "coldreload") 1725 1726 1727class DebuggerTest(Test): 1728 def __init__(self, test_path, mode): 1729 Test.__init__(self, test_path, "") 1730 self.mode = mode 1731 1732 def run(self, runner): 1733 cmd = runner.cmd_prefix + [runner.es2panda, "--module"] 1734 input_file_name = 'base.js' 1735 if self.mode == "debug-mode": 1736 cmd.extend(['--debug-info']) 1737 cmd.extend([os.path.join(self.path, input_file_name)]) 1738 cmd.extend(['--dump-assembly']) 1739 process = run_subprocess_with_beta3(self, cmd) 1740 stdout, stderr = process.communicate(timeout=runner.args.es2panda_timeout) 1741 if stderr: 1742 self.passed = False 1743 self.error = stderr.decode("utf-8", errors="ignore") 1744 return self 1745 1746 self.output = stdout.decode("utf-8", errors="ignore") 1747 1748 expected_path = os.path.join(self.path, 'expected.txt') 1749 try: 1750 with open(expected_path, 'r') as fp: 1751 expected = (''.join((fp.readlines()[12:]))).lstrip() 1752 self.passed = expected == self.output 1753 except Exception: 1754 self.passed = False 1755 1756 if not self.passed: 1757 self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\ 1758 self.output 1759 1760 if os.path.exists("base.abc"): 1761 os.remove("base.abc") 1762 1763 return self 1764 1765 1766class DebuggerRunner(Runner): 1767 def __init__(self, args): 1768 Runner.__init__(self, args, "debugger") 1769 self.test_directory = path.join(self.test_root, "debugger") 1770 self.add_test() 1771 1772 def add_test(self): 1773 self.tests = [] 1774 self.tests.append(DebuggerTest(os.path.join(self.test_directory, "debugger-in-debug"), "debug-mode")) 1775 self.tests.append(DebuggerTest(os.path.join(self.test_directory, "debugger-in-release"), "release-mode")) 1776 1777 1778class Base64Test(Test): 1779 def __init__(self, test_path, input_type): 1780 Test.__init__(self, test_path, "") 1781 self.input_type = input_type 1782 1783 def run(self, runner): 1784 cmd = runner.cmd_prefix + [runner.es2panda, "--base64Output"] 1785 if self.input_type == "file": 1786 input_file_name = 'input.js' 1787 cmd.extend(['--source-file', input_file_name]) 1788 cmd.extend([os.path.join(self.path, input_file_name)]) 1789 elif self.input_type == "string": 1790 input_file = os.path.join(self.path, "input.txt") 1791 try: 1792 with open(input_file, 'r') as fp: 1793 base64_input = (''.join((fp.readlines()[12:]))).lstrip() # ignore license description lines 1794 cmd.extend(["--base64Input", base64_input]) 1795 except Exception: 1796 self.passed = False 1797 elif self.input_type == "targetApiVersion": 1798 # base64 test for all available target api version. 1799 version = os.path.basename(self.path) 1800 cmd.extend(['--target-api-version', version]) 1801 if version == "12": 1802 cmd.append("--target-api-sub-version=beta3") 1803 input_file = os.path.join(self.path, "input.txt") 1804 try: 1805 with open(input_file, 'r') as fp: 1806 base64_input = (''.join((fp.readlines()[12:]))).lstrip() # ignore license description lines 1807 cmd.extend(["--base64Input", base64_input]) 1808 except Exception: 1809 self.passed = False 1810 else: 1811 self.error = "Unsupported base64 input type" 1812 self.passed = False 1813 return self 1814 1815 version = os.path.basename(self.path) 1816 if not self.input_type == "targetApiVersion": 1817 cmd.append("--target-api-sub-version=beta3") 1818 1819 self.log_cmd(cmd) 1820 1821 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1822 stdout, stderr = process.communicate(timeout=runner.args.es2panda_timeout) 1823 if stderr: 1824 self.passed = False 1825 self.error = stderr.decode("utf-8", errors="ignore") 1826 return self 1827 1828 self.output = stdout.decode("utf-8", errors="ignore") 1829 1830 expected_path = os.path.join(self.path, 'expected.txt') 1831 try: 1832 with open(expected_path, 'r') as fp: 1833 expected = (''.join((fp.readlines()[12:]))).lstrip() 1834 self.passed = expected == self.output 1835 except Exception: 1836 self.passed = False 1837 1838 if not self.passed: 1839 self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\ 1840 self.output 1841 1842 return self 1843 1844 1845class Base64Runner(Runner): 1846 def __init__(self, args): 1847 Runner.__init__(self, args, "Base64") 1848 self.test_directory = path.join(self.test_root, "base64") 1849 self.add_test() 1850 1851 def add_test(self): 1852 self.tests = [] 1853 self.tests.append(Base64Test(os.path.join(self.test_directory, "inputFile"), "file")) 1854 self.tests.append(Base64Test(os.path.join(self.test_directory, "inputString"), "string")) 1855 # current target api version is 12, once a new version is addded, a new testcase should be added here. 1856 current_version = 12 1857 available_target_api_versions = [9, 10, 11, current_version] 1858 for version in available_target_api_versions: 1859 self.tests.append(Base64Test(os.path.join(self.test_directory, "availableTargetApiVersion", str(version)), 1860 "targetApiVersion")) 1861 1862 def test_path(self, src): 1863 return os.path.basename(src) 1864 1865 1866class BytecodeRunner(Runner): 1867 def __init__(self, args): 1868 Runner.__init__(self, args, "Bytecode") 1869 1870 def add_directory(self, directory, extension, flags, func=Test): 1871 glob_expression = path.join( 1872 self.test_root, directory, "**/*.%s" % (extension)) 1873 files = glob(glob_expression, recursive=True) 1874 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 1875 self.tests += list(map(lambda f: func(f, flags), files)) 1876 1877 def test_path(self, src): 1878 return src 1879 1880 1881class ArkJsVmDownload: # Obtain different versions of ark_js_vm and their dependent libraries 1882 def __init__(self, args): 1883 self.build_dir = args.build_dir 1884 self.url = "https://gitee.com/zhongmingwei123123/ark_js_vm_version.git" 1885 self.local_path = path.join(self.build_dir, "ark_js_vm_version") 1886 self.max_retries = 3 1887 1888 def run_cmd_cwd(self, cmd): 1889 try: 1890 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1891 _, _ = proc.communicate() 1892 return proc.wait() 1893 except Exception as e: 1894 print(f"Error executing command: {e}") 1895 return -1 1896 1897 def git_clone(self, git_url, code_dir): 1898 cmd = ["git", "clone", git_url, code_dir, "--depth=1"] 1899 retries = 1 1900 while retries <= self.max_retries: 1901 ret = self.run_cmd_cwd(cmd) 1902 if ret == 0: 1903 break 1904 else: 1905 print(f"\n warning: Atempt: #{retries} to clone '{git_url}' failed. Try cloining again") 1906 retries += 1 1907 assert not ret, f"\n error: Cloning '{git_url}' failed." 1908 1909 def run(self): 1910 if not os.path.exists(self.local_path): 1911 print("\nstart downLoad ark_js_vm_version ...\n") 1912 self.git_clone(self.url, self.local_path) 1913 print("\ndownload finish.\n") 1914 1915class CodeDownloader: 1916 def __init__(self, args, url, components_name, max_retries=3): 1917 self.build_dir = args.build_dir 1918 self.url = url 1919 self.local_path = path.join(self.build_dir, components_name) 1920 self.max_retries = max_retries 1921 1922 def run_cmd_cwd(self, cmd): 1923 try: 1924 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1925 _, _ = proc.communicate() 1926 return proc.wait() 1927 except Exception as e: 1928 print(f"Error executing command: {e}") 1929 return -1 1930 1931 def git_clone(self, git_url, code_dir): 1932 cmd = ["git", "clone", git_url, code_dir, "--depth=1"] 1933 retries = 1 1934 while retries <= self.max_retries: 1935 ret = self.run_cmd_cwd(cmd) 1936 if ret == 0: 1937 break 1938 else: 1939 print(f"\nWarning: Attempt #{retries} to clone '{git_url}' failed. Retrying...") 1940 retries += 1 1941 assert ret == 0, f"\nError: Cloning '{git_url}' failed." 1942 1943 def run(self): 1944 if not os.path.exists(self.local_path): 1945 print(f"\nStart downloading {self.url}...\n") 1946 self.git_clone(self.url, self.local_path) 1947 print("\nDownload finished.\n") 1948 print(self.local_path) 1949 1950class AbcTestCasesPrepare: 1951 def __init__(self, args): 1952 self.test_root = path.dirname(path.abspath(__file__)) 1953 self.es2panda = path.join(args.build_dir, "es2abc") 1954 self.args = args 1955 self.valid_mode_list = ["non_merge_mode", "merge_mode"] 1956 self.test_abc_path_list = set() 1957 1958 @staticmethod 1959 def split_api_version(version_str): 1960 parts = version_str.split("API")[1].split("beta") 1961 main_part = parts[0] 1962 beta_part = "beta%s" % parts[1] if len(parts) > 1 else "" 1963 return (main_part, beta_part) 1964 1965 def add_abc_directory(self, directory, extension): 1966 test_directory = path.join(self.test_root, directory) 1967 glob_expression = path.join(test_directory, "*.%s" % (extension)) 1968 files = glob(glob_expression) 1969 files = fnmatch.filter(files, self.test_root + "**" + self.args.filter) 1970 return files 1971 1972 def get_output_path(self, source_path, main_version, beta_version, es2abc_version): 1973 base = path.splitext(source_path)[0] 1974 suffix = f"version_API{main_version}{beta_version}" 1975 return f"{base}_{es2abc_version}_{suffix}.abc" if es2abc_version != "default" else f"{base}_{suffix}.abc" 1976 1977 def gen_abc_versions(self, flags, source_path, es2abc_version): 1978 supported_apis = ES2ABC_API_SUPPORT.get(es2abc_version) 1979 for api_version in supported_apis: 1980 main_version, beta_version = AbcTestCasesPrepare.split_api_version(api_version) 1981 output_path = self.get_output_path(source_path, main_version, beta_version, es2abc_version) 1982 self.test_abc_path_list.add(output_path) 1983 _, stderr = self.compile_for_target_version(flags, source_path, output_path, main_version, beta_version, es2abc_version) 1984 if stderr: 1985 raise RuntimeError(f"abc generate error: {stderr}") 1986 1987 def gen_abc_tests(self, directory, extension, flags, abc_mode, es2abc_versions=["default"]): 1988 if abc_mode not in self.valid_mode_list: 1989 raise ValueError(f"Invalid abc_mode value: {abc_mode}") 1990 test_source_list = self.add_abc_directory(directory, extension) 1991 1992 for es2abc_version in es2abc_versions: 1993 for input_path in test_source_list: 1994 self.gen_abc_versions(flags, input_path, es2abc_version) 1995 1996 def compile_for_target_version(self, flags, input_path, output_path, target_api_version, target_api_sub_version="", es2abc_version="default"): 1997 es2panda_path = ( 1998 self.es2panda if es2abc_version == "default" 1999 else os.path.join(self.args.build_dir, "es2abc_version", es2abc_version, "es2abc") 2000 ) 2001 cmd = [] 2002 cmd.append(es2panda_path) 2003 cmd.append(input_path) 2004 cmd.extend(flags) 2005 cmd.append("--target-api-version=%s" % (target_api_version)) 2006 cmd.extend(["--output=%s" % (output_path)]) 2007 if target_api_sub_version != "": 2008 cmd.append("--target-api-sub-version=%s" % (target_api_sub_version)) 2009 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 2010 stdout, stderr = process.communicate(timeout=10) 2011 if stderr: 2012 stderr = "Error executing command: %s\n%s" % (cmd, stderr) 2013 return stdout, stderr 2014 2015 def remove_abc_tests(self): 2016 for abc_path in self.test_abc_path_list: 2017 if path.exists(abc_path): 2018 os.remove(abc_path) 2019 2020 2021class AbcVersionControlRunner(Runner): 2022 def __init__(self, args): 2023 super().__init__(args, "AbcVersionControl") 2024 self.valid_mode_list = ["non_merge_mode", "merge_mode", "mix_compile_mode"] 2025 2026 def add_directory(self, directory, extension, flags, abc_mode, is_discard=False): 2027 if abc_mode not in self.valid_mode_list: 2028 raise ValueError(f"Invalid abc_mode value: {abc_mode}") 2029 glob_expression = path.join(self.test_root, directory, "*.%s" % (extension)) 2030 files = glob(glob_expression) 2031 files = fnmatch.filter(files, self.test_root + "**" + self.args.filter) 2032 if abc_mode == "mix_compile_mode": 2033 files = [f for f in files if not f.endswith("-expected.txt")] 2034 self.tests += list(map(lambda f: TestAbcVersionControl(f, flags, abc_mode, is_discard), files)) 2035 2036 def test_path(self, src): 2037 return src 2038 2039 def run(self): 2040 for test in self.tests: 2041 test.run(self) 2042 2043 2044class VersionControlRunner(Runner): 2045 def __init__(self, args): 2046 Runner.__init__(self, args, "VersionControl") 2047 2048 def add_directory(self, directory, extension, flags, test_version, feature_type, module_dir=None, func=Test): 2049 glob_expression = path.join(self.test_root, directory, "*.%s" % (extension)) 2050 files = glob(glob_expression) 2051 files = fnmatch.filter(files, self.test_root + "**" + self.args.filter) 2052 module_path_list = [] 2053 if module_dir is not None: 2054 module_path_list = self.add_module_path(module_dir) 2055 self.tests += list( 2056 map(lambda f: TestVersionControl(f, flags, test_version, feature_type, module_path_list), files) 2057 ) 2058 2059 def add_module_path(self, module_dir): 2060 module_path_list = [] 2061 glob_expression_ts = path.join(self.test_root, module_dir, "*.%s" % ("ts")) 2062 glob_expression_js = path.join(self.test_root, module_dir, "*.%s" % ("js")) 2063 module_path_list = glob(glob_expression_ts) 2064 module_path_list.extend(glob(glob_expression_js)) 2065 module_path_list = fnmatch.filter(module_path_list, self.test_root + "**" + self.args.filter) 2066 return module_path_list 2067 2068 def test_path(self, src): 2069 return src 2070 2071 def run(self): 2072 for test in self.tests: 2073 test.run(self) 2074 2075 2076class TestAbcVersionControl(Test): 2077 def __init__(self, test_path, flags, abc_mode, is_discard): 2078 super().__init__(test_path, flags) 2079 self.min_support_version_number = API_VERSION_MAP.get(MIN_SUPPORT_BC_VERSION) 2080 self.abc_mode = abc_mode 2081 self.is_discard = is_discard 2082 self.output = None 2083 self.process = None 2084 self.is_support = False 2085 self.test_abc_list = list() 2086 self.test_input = None 2087 self.target_abc_path = None 2088 self.entry_point = self.get_entry_point() 2089 2090 @staticmethod 2091 def compare_version_number(version1, version2): 2092 v1 = TestAbcVersionControl.version_number_to_tuple(version1) 2093 v2 = TestAbcVersionControl.version_number_to_tuple(version2) 2094 for num1, num2 in zip(v1, v2): 2095 if num1 > num2: 2096 return 1 2097 elif num1 < num2: 2098 return -1 2099 return 0 2100 2101 @staticmethod 2102 def version_number_to_tuple(version): 2103 return tuple(int(part) for part in version.split(".")) 2104 2105 def get_entry_point(self): 2106 if self.abc_mode == "merge_mode": 2107 base_name = os.path.basename(self.path) 2108 return os.path.splitext(base_name)[0] 2109 elif self.abc_mode == "mix_compile_mode": 2110 return MIX_COMPILE_ENTRY_POINT 2111 return "" 2112 2113 def get_path_to_expected(self, is_support=False, test_stage=""): 2114 support_name = "supported_" if is_support else "unsupported_" 2115 if self.abc_mode == "mix_compile_mode" and test_stage != "runtime": 2116 support_name = "" 2117 expected_name = path.splitext(self.path)[0].split("_version_API")[0] 2118 expected_name = re.sub(r"_(\d+\.\d+)(?=(_|$))", "", expected_name) 2119 expected_path = "%s_%s%s-expected.txt" % (expected_name, support_name, test_stage) 2120 return expected_path 2121 2122 def run_process(self, cmd): 2123 self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 2124 stdout, stderr = self.process.communicate() 2125 self.output = stdout.decode("utf-8", errors="ignore") + stderr.decode("utf-8", errors="ignore").split("\n")[0] 2126 if stderr: 2127 stderr = "Error executing command: %s\n%s" % (cmd, stderr) 2128 return stdout, stderr 2129 2130 def compile_for_target_version( 2131 self, runner, input_path, output_path, target_api_version, target_api_sub_version="" 2132 ): 2133 cmd = [] 2134 cmd.append(runner.es2panda) 2135 if self.abc_mode == "mix_compile_mode": 2136 input_path = "@%s" % (input_path) 2137 cmd.append(input_path) 2138 cmd.extend(self.flags) 2139 cmd.append("--target-api-version=%s" % (target_api_version)) 2140 cmd.extend(["--output=%s" % (output_path)]) 2141 if target_api_sub_version != "": 2142 cmd.append("--target-api-sub-version=%s" % (target_api_sub_version)) 2143 self.log_cmd(cmd) 2144 stdout, stderr = self.run_process(cmd) 2145 return stdout, stderr 2146 2147 def build_abc_filename(self, target_api_version, target_api_sub_version): 2148 prefix = f"{path.splitext(self.path)[0]}" 2149 abc_name = f"{prefix}_target_{target_api_version}{target_api_sub_version}.abc" 2150 return abc_name.replace("/", "_") 2151 2152 def extract_input_versions(self): 2153 input_api_versions = self.extract_api_versions(path.split(self.path)[1]) 2154 input_version_numbers = [API_VERSION_MAP.get(api) for api in input_api_versions] 2155 return sorted(input_version_numbers, key=TestAbcVersionControl.version_number_to_tuple) 2156 2157 def determine_expected_behavior(self, target_api_version, target_api_sub_version, input_version_numbers): 2158 target_version = "API" + target_api_version + target_api_sub_version 2159 target_version_number = API_VERSION_MAP.get(target_version) 2160 2161 min_input_version_number = input_version_numbers[0] 2162 max_input_version_number = input_version_numbers[-1] 2163 2164 if TestAbcVersionControl.compare_version_number(target_version_number, self.min_support_version_number) < 0: 2165 compile_expected_path = self.get_path_to_expected( 2166 False, "compile_target_version_below_min_support" 2167 ) 2168 return False, compile_expected_path, target_api_version 2169 2170 elif ( 2171 TestAbcVersionControl.compare_version_number(min_input_version_number, self.min_support_version_number) < 0 2172 ): 2173 compile_expected_path = self.get_path_to_expected(False, "compile_cur_version_below_min_support") 2174 return False, compile_expected_path, self.path 2175 2176 elif TestAbcVersionControl.compare_version_number(target_version_number, max_input_version_number) < 0: 2177 compile_expected_path = self.get_path_to_expected(False, "compile_target_version_below_cur") 2178 return False, compile_expected_path, self.path 2179 2180 elif self.is_discard: 2181 compile_expected_path = self.get_path_to_expected(False, "compile_discard") 2182 return False, compile_expected_path, "" 2183 2184 return True, None, None 2185 2186 def generate_abc(self, runner, target_api_version, target_api_sub_version=""): 2187 compile_expected_path = None 2188 target_abc_name = self.build_abc_filename(target_api_version, target_api_sub_version) 2189 self.target_abc_path = path.join(runner.build_dir, target_abc_name) 2190 2191 _, stderr = self.compile_for_target_version( 2192 runner, self.path, self.target_abc_path, target_api_version, target_api_sub_version 2193 ) 2194 format_content = "" 2195 self.is_support = False 2196 2197 # Extract the API versions of the input abc files from the file name of the test case. 2198 input_version_numbers = self.extract_input_versions() 2199 2200 is_support, compile_expected_path, format_content = self.determine_expected_behavior( 2201 target_api_version, target_api_sub_version, input_version_numbers 2202 ) 2203 self.is_support = is_support 2204 if self.is_support: 2205 if stderr: 2206 self.passed = False 2207 else: 2208 self.passed = True 2209 return stderr 2210 2211 try: 2212 with open(compile_expected_path, "r") as fp: 2213 expected = fp.read() 2214 self.passed = expected.format(format_content) in self.output and self.process.returncode in [0, 1] 2215 except Exception: 2216 self.passed = False 2217 return stderr 2218 2219 def execute_abc(self, runner, vm_version, entry_point=""): 2220 cmd = [] 2221 ark_js_vm_dir = os.path.join( 2222 runner.build_dir, 2223 "ark_js_vm_version", 2224 vm_version, 2225 ) 2226 ld_library_path = os.path.join(ark_js_vm_dir, "lib") 2227 os.environ["LD_LIBRARY_PATH"] = ld_library_path 2228 ark_js_vm_path = os.path.join(ark_js_vm_dir, "ark_js_vm") 2229 cmd.append(ark_js_vm_path) 2230 if entry_point != "": 2231 cmd.append("--entry-point=%s" % entry_point) 2232 cmd.append(self.target_abc_path) 2233 self.log_cmd(cmd) 2234 stdout, stderr = self.run_process(cmd) 2235 return stdout, stderr 2236 2237 def test_abc_execution(self, runner, target_api_version, target_api_sub_version=""): 2238 stderr = None 2239 target_version = "API" + target_api_version + target_api_sub_version 2240 target_version_number = API_VERSION_MAP.get(target_version) 2241 for vm_version in ARK_JS_VM_LIST: 2242 vm_version_number = API_VERSION_MAP.get(vm_version) 2243 _, stderr = self.execute_abc(runner, vm_version, self.entry_point) 2244 self.is_support = ( 2245 TestAbcVersionControl.compare_version_number(vm_version_number, target_version_number) >= 0 2246 ) 2247 runtime_expect_path = self.get_path_to_expected(self.is_support, "runtime") 2248 try: 2249 with open(runtime_expect_path, "r") as fp: 2250 expected = fp.read() 2251 if self.is_support and self.abc_mode != "merge_mode": 2252 self.passed = expected == self.output and self.process.returncode in [0, 1, 255] 2253 else: 2254 self.passed = expected in self.output 2255 pass 2256 except Exception: 2257 self.passed = False 2258 if not self.passed: 2259 return stderr 2260 return stderr 2261 2262 def extract_api_versions(self, file_name): 2263 pattern = r"(API\d+)(beta\d+)?" 2264 matches = re.findall(pattern, file_name) 2265 api_versions = [f"{api}{f'{beta}' if beta else ''}" for api, beta in matches] 2266 return api_versions 2267 2268 def remove_abc(self, abc_path): 2269 if path.exists(abc_path): 2270 os.remove(abc_path) 2271 2272 def run(self, runner): 2273 for api_version in API_VERSION_MAP: 2274 target_api_version, target_api_sub_version = AbcTestCasesPrepare.split_api_version(api_version) 2275 stderr = self.generate_abc(runner, target_api_version, target_api_sub_version) 2276 if not self.passed: 2277 self.error = stderr 2278 return self 2279 if stderr: 2280 continue 2281 stderr = self.test_abc_execution(runner, target_api_version, target_api_sub_version) 2282 self.remove_abc(self.target_abc_path) 2283 if not self.passed: 2284 self.error = stderr 2285 return self 2286 return self 2287 2288class Es2abcVersionControlRunner(Runner): 2289 def __init__(self, args): 2290 super().__init__(args, "Es2abcVersionControl") 2291 self.valid_mode_list = ["non_merge_mode", "merge_mode"] 2292 2293 def add_directory(self, directory, extension, flags, abc_mode, is_discard=False): 2294 if abc_mode not in self.valid_mode_list: 2295 raise ValueError(f"Invalid abc_mode value: {abc_mode}") 2296 glob_expression = path.join(self.test_root, directory, "*.%s" % (extension)) 2297 files = glob(glob_expression) 2298 files = fnmatch.filter(files, self.test_root + "**" + self.args.filter) 2299 self.tests += list(map(lambda f: TestEs2abcVersionControl(f, flags, abc_mode, is_discard), files)) 2300 2301 def test_path(self, src): 2302 return src 2303 2304 def run(self): 2305 for test in self.tests: 2306 test.run(self) 2307 self.args.abc_tests_prepare.remove_abc_tests() 2308 2309class TestEs2abcVersionControl(TestAbcVersionControl): 2310 def __init__(self, test_path, flags, abc_mode, is_discard, es2abc_versions=None): 2311 super().__init__(test_path, flags, abc_mode, is_discard) 2312 self.es2abc_versions = es2abc_versions or list(ES2ABC_API_SUPPORT.keys()) 2313 2314 @staticmethod 2315 def version_str_to_tuple(version: str): 2316 if isinstance(version, list): 2317 version = ".".join(version) 2318 return tuple(int(x) for x in version.split(".")) 2319 2320 @classmethod 2321 def get_max_supported_version(cls, es2abc_version: str) -> str: 2322 supported_apis = ES2ABC_API_SUPPORT.get(es2abc_version, ES2ABC_API_SUPPORT["default"]) 2323 max_api = max(supported_apis, key=lambda api: cls.version_str_to_tuple(API_VERSION_MAP[api])) 2324 return API_VERSION_MAP[max_api] 2325 2326 @classmethod 2327 def should_skip_abc_file(cls, file_version: str, es2abc_version: str) -> bool: 2328 max_supported_version = cls.get_max_supported_version(es2abc_version) 2329 return cls.version_str_to_tuple(file_version) > cls.version_str_to_tuple(max_supported_version) 2330 2331 def build_abc_filename(self, target_api_version, target_api_sub_version, es2abc_version): 2332 prefix = f"{path.splitext(self.path)[0]}" 2333 if es2abc_version == "default" or es2abc_version is None: 2334 abc_name = f"{prefix}_target_{target_api_version}{target_api_sub_version}.abc" 2335 else: 2336 abc_name = f"{prefix}_{es2abc_version}_target_{target_api_version}{target_api_sub_version}.abc" 2337 return abc_name.replace("/", "_") 2338 2339 def compile_for_target_version( 2340 self, runner, input_path, output_path, target_api_version, target_api_sub_version="", es2abc_version="default" 2341 ): 2342 cmd = [] 2343 if es2abc_version != "default": 2344 runner.es2panda = os.path.join(runner.build_dir, "es2abc_version", es2abc_version, "es2abc") 2345 cmd.append(runner.es2panda) 2346 cmd.append(input_path) 2347 cmd.extend(self.flags) 2348 cmd.append("--target-api-version=%s" % target_api_version) 2349 cmd.extend(["--output=%s" % output_path]) 2350 if target_api_sub_version: 2351 cmd.append("--target-api-sub-version=%s" % target_api_sub_version) 2352 self.es2abc_cmd = ' '.join(cmd) 2353 stdout, stderr = self.run_process(cmd) 2354 return stdout, stderr 2355 2356 def generate_abc(self, runner, target_api_version, target_api_sub_version=""): 2357 compile_expected_path = None 2358 input_version_numbers = self.extract_input_versions() 2359 2360 for es2abc_version in self.es2abc_versions: 2361 target_abc_name = self.build_abc_filename(target_api_version, target_api_sub_version, es2abc_version) 2362 self.target_abc_path = path.join(runner.build_dir, target_abc_name) 2363 2364 _, stderr = self.compile_for_target_version( 2365 runner, self.path, self.target_abc_path, target_api_version, target_api_sub_version, es2abc_version 2366 ) 2367 2368 format_content = "" 2369 self.is_support = False 2370 2371 # The version of the abc file exceeds the maximum supported range of es2abc 2372 if self.should_skip_abc_file(input_version_numbers, es2abc_version): 2373 continue 2374 2375 is_support, compile_expected_path, format_content = self.determine_expected_behavior( 2376 target_api_version, target_api_sub_version, input_version_numbers 2377 ) 2378 self.is_support = is_support 2379 if self.is_support: 2380 if stderr: 2381 self.passed = False 2382 else: 2383 self.passed = True 2384 return stderr 2385 2386 try: 2387 with open(compile_expected_path, "r") as fp: 2388 expected = fp.read() 2389 self.passed = expected.format(format_content) in self.output and self.process.returncode in [0, 1] 2390 except Exception: 2391 self.passed = False 2392 return stderr 2393 return None 2394 2395 def run(self, runner): 2396 for api_version in API_VERSION_MAP: 2397 target_api_version, target_api_sub_version = AbcTestCasesPrepare.split_api_version(api_version) 2398 stderr = self.generate_abc(runner, target_api_version, target_api_sub_version) 2399 if not self.passed: 2400 self.error = stderr 2401 return self 2402 return self 2403 2404class TestVersionControl(Test): 2405 def __init__(self, test_path, flags, test_version, feature_type, module_path_list): 2406 Test.__init__(self, test_path, flags) 2407 self.beta_version_default = 3 2408 self.version_with_sub_version_list = ["12"] 2409 self.target_api_version_list = ["9", "10", "11", "12", "18", "20"] 2410 self.target_api_sub_version_list = ["beta1", "beta2", "beta3"] 2411 self.specific_api_version_list = ["API18", "API12beta3", "API11"] 2412 self.output = None 2413 self.process = None 2414 self.test_version = test_version 2415 self.test_abc_path = None 2416 self.feature_type = feature_type 2417 self.module_path_list = module_path_list 2418 self.module_abc_path_set = set() 2419 2420 def split_version(self, version_str): 2421 parts = version_str.split("API")[1].split("beta") 2422 main_part = int(parts[0]) 2423 beta_part = int(parts[1]) if len(parts) > 1 else self.beta_version_default 2424 return (main_part, beta_part) 2425 2426 def compare_two_versions(self, version1, version2): 2427 version1_parsed = self.split_version(version1) 2428 version2_parsed = self.split_version(version2) 2429 2430 if version1_parsed < version2_parsed: 2431 return -1 2432 elif version1_parsed > version2_parsed: 2433 return 1 2434 else: 2435 return 0 2436 2437 def get_relative_path(self, from_dir, to_dir): 2438 from_dir = os.path.normpath(from_dir) 2439 to_dir = os.path.normpath(to_dir) 2440 from_dir = os.path.abspath(from_dir) 2441 to_dir = os.path.abspath(to_dir) 2442 from_parts = from_dir.split(os.sep) 2443 to_parts = to_dir.split(os.sep) 2444 common_prefix_length = 0 2445 for part1, part2 in zip(from_parts, to_parts): 2446 if part1 == part2: 2447 common_prefix_length += 1 2448 else: 2449 break 2450 relative_parts = [".."] * (len(from_parts) - common_prefix_length) + to_parts[common_prefix_length:] 2451 relative_path = os.path.join(*relative_parts) 2452 return relative_path 2453 2454 def generate_single_module_abc(self, runner, module_path, target_version): 2455 cmd = [] 2456 cmd.append(runner.es2panda) 2457 cmd.append(module_path) 2458 cmd.append("--module") 2459 main_version, sub_version = self.split_version(target_version) 2460 cmd.append("--target-api-version=%s" % (main_version)) 2461 if main_version == 12: 2462 cmd.append("--target-api-sub-version=beta%s" % (sub_version)) 2463 2464 basename = os.path.basename(module_path) 2465 module_abc_name = "%s.abc" % (path.splitext(basename)[0]) 2466 relative_path = self.get_relative_path(path.split(self.path)[0], path.split(module_path)[0]) 2467 module_abc_dir = path.join(runner.build_dir, relative_path) 2468 if not os.path.exists(module_abc_dir): 2469 os.makedirs(module_abc_dir) 2470 module_abc_path = path.join(module_abc_dir, module_abc_name) 2471 self.module_abc_path_set.add(module_abc_path) 2472 cmd.extend(["--output=%s" % (module_abc_path)]) 2473 2474 self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 2475 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 2476 _, stderr = proc.communicate() 2477 proc.wait() 2478 if stderr: 2479 print(stderr.decode("utf-8", errors="ignore")) 2480 2481 def generate_module_abc(self, runner, target_version): 2482 for module_path in self.module_path_list: 2483 self.generate_single_module_abc(runner, module_path, target_version) 2484 2485 def remove_module_abc(self): 2486 for module_abc_path in self.module_abc_path_set: 2487 if path.exists(module_abc_path): 2488 os.remove(module_abc_path) 2489 2490 def get_path_to_expected( 2491 self, is_support, expected_stage, target_api_version="", specific_api_version="", dump_type="" 2492 ): 2493 support_name = "supported" if is_support else "unsupported" 2494 api_name = "" 2495 # Higher than the specific API version, expected results may differ 2496 if target_api_version != "" and specific_api_version != "": 2497 if self.compare_two_versions(target_api_version, specific_api_version) >= 0: 2498 api_name = "for_higher_or_equal_to_%s_" % (specific_api_version) 2499 else: 2500 api_name = "for_below_%s_" % (specific_api_version) 2501 if dump_type == "ast": 2502 dump_type = "" 2503 elif dump_type == "asm": 2504 dump_type = "asm_" 2505 expected_path = "%s_%s_%s_%s%sversion-expected.txt" % ( 2506 path.splitext(self.path)[0], 2507 support_name, 2508 expected_stage, 2509 api_name, 2510 dump_type, 2511 ) 2512 return expected_path 2513 2514 def get_path_to_runtime_output_below_version_expected(self): 2515 expected_path = "%s_runtime_below_abc_api_version-expected.txt" % ( 2516 path.splitext(self.path)[0]) 2517 return expected_path 2518 2519 def get_path_to_runtime_output_expected(self, is_support, target_api_version, is_below_abc_api_version): 2520 path_expected = None 2521 if is_below_abc_api_version: 2522 path_expected = self.get_path_to_runtime_output_below_version_expected() 2523 return path_expected 2524 for specific_api_version in self.specific_api_version_list: 2525 if self.compare_two_versions(target_api_version, specific_api_version) > 0: 2526 continue 2527 path_expected = self.get_path_to_expected(is_support, "runtime", target_api_version, specific_api_version) 2528 if path.exists(path_expected): 2529 return path_expected 2530 return self.get_path_to_expected(is_support, "runtime", target_api_version) 2531 2532 def get_path_to_compile_ast_output_expected(self, is_support): 2533 return self.get_path_to_expected(is_support, "compile") 2534 2535 def get_path_to_compile_asm_output_expected(self, is_support, target_api_version): 2536 path_expected = None 2537 for specific_api_version in self.specific_api_version_list: 2538 path_expected = self.get_path_to_expected( 2539 is_support, "compile", target_api_version, specific_api_version, "asm" 2540 ) 2541 if path.exists(path_expected): 2542 return path_expected 2543 return self.get_path_to_expected(is_support, "compile", "", "", "asm") 2544 2545 def run_process(self, cmd): 2546 self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 2547 stdout, stderr = self.process.communicate() 2548 self.output = stdout.decode("utf-8", errors="ignore") + stderr.decode("utf-8", errors="ignore") 2549 return stdout, stderr 2550 2551 def run_process_compile(self, runner, target_api_version, target_api_sub_version="bata3", dump_type=""): 2552 cmd = [] 2553 cmd.append(runner.es2panda) 2554 cmd.append(self.path) 2555 cmd.extend(self.flags) 2556 cmd.append("--target-api-version=%s" % (target_api_version)) 2557 test_abc_name = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_") 2558 self.test_abc_path = path.join(runner.build_dir, test_abc_name) 2559 cmd.extend(["--output=%s" % (self.test_abc_path)]) 2560 if target_api_version == "12": 2561 cmd.append("--target-api-sub-version=%s" % (target_api_sub_version)) 2562 if dump_type == "ast": 2563 cmd.append("--dump-ast") 2564 elif dump_type == "assembly": 2565 cmd.append("--dump-assembly") 2566 self.log_cmd(cmd) 2567 stdout, stderr = self.run_process(cmd) 2568 return stdout, stderr 2569 2570 def generate_ast_of_target_version(self, runner, target_api_version, target_api_sub_version="bata3"): 2571 return self.run_process_compile(runner, target_api_version, target_api_sub_version, dump_type="ast") 2572 2573 def generate_asm_of_target_version(self, runner, target_api_version, target_api_sub_version="bata3"): 2574 return self.run_process_compile(runner, target_api_version, target_api_sub_version, dump_type="assembly") 2575 2576 def runtime_for_target_version(self, runner, target_api_version, target_api_sub_version="bata3"): 2577 cmd = [] 2578 if target_api_version != "12": 2579 target_api_sub_version = "" 2580 # there is no virtual machine with version api12beta2 available. 2581 # We have chosen api12beta1 as a replacement. 2582 if target_api_version == "12" and target_api_sub_version == "beta2": 2583 target_api_sub_version = "beta1" 2584 ark_js_vm_dir = os.path.join( 2585 runner.build_dir, 2586 "ark_js_vm_version", 2587 "API%s%s" % (target_api_version, target_api_sub_version), 2588 ) 2589 ld_library_path = os.path.join(ark_js_vm_dir, "lib") 2590 os.environ["LD_LIBRARY_PATH"] = ld_library_path 2591 ark_js_vm_path = os.path.join(ark_js_vm_dir, "ark_js_vm") 2592 cmd.append(ark_js_vm_path) 2593 cmd.append(self.test_abc_path) 2594 self.log_cmd(cmd) 2595 self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 2596 stdout, stderr = self.process.communicate() 2597 self.output = stdout.decode("utf-8", errors="ignore") + stderr.decode("utf-8", errors="ignore").split("\n")[0] 2598 return stdout, stderr 2599 2600 def run_for_single_version(self, runner, target_api_version, target_api_sub_version=""): 2601 cur_api_version = "API" + target_api_version + target_api_sub_version 2602 is_support = True if self.compare_two_versions(cur_api_version, self.test_version) >= 0 else False 2603 compile_expected_path = None 2604 stderr = None 2605 if self.feature_type == "syntax_feature": 2606 compile_expected_path = self.get_path_to_compile_ast_output_expected(is_support) 2607 _, stderr = self.generate_ast_of_target_version(runner, target_api_version, target_api_sub_version) 2608 elif self.feature_type == "bytecode_feature": 2609 compile_expected_path = self.get_path_to_compile_asm_output_expected(is_support, cur_api_version) 2610 _, stderr = self.generate_asm_of_target_version( 2611 runner, target_api_version, target_api_sub_version 2612 ) 2613 try: 2614 with open(compile_expected_path, "r") as fp: 2615 expected = fp.read() 2616 self.passed = expected == self.output and self.process.returncode in [0, 1] 2617 except Exception: 2618 self.passed = False 2619 if not self.passed or (stderr and self.passed): 2620 return stderr 2621 cur_api_version_number = API_VERSION_MAP.get(cur_api_version) 2622 for api_version in self.target_api_version_list: 2623 # The interception capability of API9 version of ark_js_vm has not yet been launched. 2624 if api_version == "9": 2625 continue 2626 for api_sub_version in self.target_api_sub_version_list: 2627 if not api_version in self.version_with_sub_version_list and api_sub_version != "beta3": 2628 continue 2629 elif not api_version in self.version_with_sub_version_list: 2630 api_sub_version = "" 2631 cur_runtime_api_version = "API" + api_version + api_sub_version 2632 cur_runtime_api_version_number = API_VERSION_MAP.get(cur_runtime_api_version) 2633 is_below_abc_version = ( 2634 False 2635 if TestAbcVersionControl.compare_version_number( 2636 cur_runtime_api_version_number, 2637 cur_api_version_number, 2638 ) 2639 >= 0 2640 else True 2641 ) 2642 self.generate_module_abc(runner, cur_runtime_api_version) 2643 _, stderr = self.runtime_for_target_version(runner, api_version, api_sub_version) 2644 runtime_expected_path = self.get_path_to_runtime_output_expected( 2645 is_support, cur_api_version, is_below_abc_version 2646 ) 2647 self.remove_module_abc() 2648 try: 2649 with open(runtime_expected_path, "r") as fp: 2650 expected = fp.read() 2651 if is_below_abc_version: 2652 self.passed = expected in self.output 2653 else: 2654 self.passed = expected == self.output 2655 except Exception: 2656 self.passed = False 2657 if not self.passed: 2658 return stderr 2659 return stderr 2660 2661 def run(self, runner): 2662 for target_api_version in self.target_api_version_list: 2663 stderr = None 2664 if target_api_version == "12": 2665 for target_api_sub_version in self.target_api_sub_version_list: 2666 stderr = self.run_for_single_version(runner, target_api_version, target_api_sub_version) 2667 if path.exists(self.test_abc_path): 2668 os.remove(self.test_abc_path) 2669 if not self.passed: 2670 self.error = stderr.decode("utf-8", errors="ignore") 2671 return self 2672 else: 2673 stderr = self.run_for_single_version(runner, target_api_version) 2674 if not self.passed: 2675 self.error = stderr.decode("utf-8", errors="ignore") 2676 return self 2677 return self 2678 2679 2680class CompilerTestInfo(object): 2681 def __init__(self, directory, extension, flags): 2682 self.directory = directory 2683 self.extension = extension 2684 self.flags = flags 2685 2686 def update_dir(self, prefiex_dir): 2687 self.directory = os.path.sep.join([prefiex_dir, self.directory]) 2688 2689 2690# Copy compiler directory to test/.local directory, and do inplace obfuscation. 2691def prepare_for_obfuscation(compiler_test_infos, test_root): 2692 tmp_dir_name = ".local" 2693 tmp_path = os.path.join(test_root, tmp_dir_name) 2694 if not os.path.exists(tmp_path): 2695 os.mkdir(tmp_path) 2696 2697 test_root_dirs = set() 2698 for info in compiler_test_infos: 2699 root_dir = info.directory.split("/")[0] 2700 test_root_dirs.add(root_dir) 2701 2702 for test_dir in test_root_dirs: 2703 src_dir = os.path.join(test_root, test_dir) 2704 target_dir = os.path.join(tmp_path, test_dir) 2705 if os.path.exists(target_dir): 2706 shutil.rmtree(target_dir) 2707 shutil.copytree(src_dir, target_dir) 2708 2709 for info in compiler_test_infos: 2710 info.update_dir(tmp_dir_name) 2711 2712 2713def add_directory_for_version_control(runners, args): 2714 tools = [ 2715 { 2716 "url": "https://gitee.com/zhongmingwei123123/ark_js_vm_version.git", 2717 "components_name": "ark_js_vm_version" 2718 }, 2719 { 2720 "url": "https://gitee.com/li_yue1999/es2abc_version.git", 2721 "components_name": "es2abc_version" 2722 } 2723 ] 2724 2725 for tool in tools: 2726 downloader = CodeDownloader(args, tool['url'], tool['components_name']) 2727 downloader.run() 2728 2729 runner = VersionControlRunner(args) 2730 runner.add_directory( 2731 "version_control/API11/syntax_feature", 2732 "js", 2733 ["--module"], 2734 "API11", 2735 "syntax_feature", 2736 ) 2737 runner.add_directory( 2738 "version_control/API11/syntax_feature", 2739 "ts", 2740 ["--module"], 2741 "API11", 2742 "syntax_feature", 2743 ) 2744 runner.add_directory( 2745 "version_control/API12beta1_and_beta2/syntax_feature", 2746 "ts", ["--module"], 2747 "API12beta1", 2748 "syntax_feature", 2749 ) 2750 runner.add_directory( 2751 "version_control/API12beta1_and_beta2/syntax_feature", 2752 "js", 2753 ["--module"], 2754 "API12beta1", 2755 "syntax_feature", 2756 ) 2757 runner.add_directory( 2758 "version_control/API12beta3/syntax_feature", 2759 "ts", 2760 ["--module"], 2761 "API12beta3", 2762 "syntax_feature", 2763 "version_control/API12beta3/syntax_feature/import_target", 2764 ) 2765 runner.add_directory( 2766 "version_control/API12beta3/syntax_feature", 2767 "js", 2768 ["--module"], 2769 "API12beta3", 2770 "syntax_feature", 2771 "version_control/API12beta3/syntax_feature/import_target", 2772 ) 2773 runner.add_directory( 2774 "version_control/API11/bytecode_feature", 2775 "ts", 2776 ["--module"], 2777 "API11", 2778 "bytecode_feature", 2779 ) 2780 runner.add_directory( 2781 "version_control/API11/bytecode_feature", 2782 "js", 2783 ["--module"], 2784 "API11", 2785 "bytecode_feature", 2786 ) 2787 runner.add_directory( 2788 "version_control/API12beta1_and_beta2/bytecode_feature", 2789 "ts", 2790 ["--module"], 2791 "API12beta1", 2792 "bytecode_feature", 2793 "version_control/API12beta1_and_beta2/bytecode_feature/import_target", 2794 ) 2795 runner.add_directory( 2796 "version_control/API12beta1_and_beta2/bytecode_feature", 2797 "js", 2798 ["--module"], 2799 "API12beta1", 2800 "bytecode_feature", 2801 "version_control/API12beta1_and_beta2/bytecode_feature/import_target", 2802 ) 2803 runner.add_directory( 2804 "version_control/API12beta3/bytecode_feature", 2805 "ts", 2806 ["--module"], 2807 "API12beta3", 2808 "bytecode_feature", 2809 "version_control/API12beta3/bytecode_feature/import_target", 2810 ) 2811 runner.add_directory( 2812 "version_control/API12beta3/bytecode_feature", 2813 "js", 2814 ["--module"], 2815 "API12beta3", 2816 "bytecode_feature", 2817 "version_control/API12beta3/bytecode_feature/import_target", 2818 ) 2819 runner.add_directory( 2820 "version_control/API18/bytecode_feature", 2821 "js", 2822 [], 2823 "API18", 2824 "bytecode_feature", 2825 ) 2826 runner.add_directory( 2827 "version_control/API18/bytecode_feature", 2828 "ts", 2829 ["--module"], 2830 "API18", 2831 "bytecode_feature", 2832 ) 2833 runner.add_directory( 2834 "version_control/API20/bytecode_feature", 2835 "ts", 2836 ["--module", "--enable-annotations"], 2837 "API20", 2838 "bytecode_feature", 2839 ) 2840 runners.append(runner) 2841 2842 abc_tests_prepare = AbcTestCasesPrepare(args) 2843 es2abc_versions = list(ES2ABC_API_SUPPORT.keys()) 2844 2845 abc_tests_prepare.gen_abc_tests( 2846 "version_control/bytecode_version_control/non_merge_mode", 2847 "js", 2848 ["--module"], 2849 "non_merge_mode", 2850 es2abc_versions 2851 ) 2852 abc_tests_prepare.gen_abc_tests( 2853 "version_control/bytecode_version_control/merge_mode", 2854 "js", 2855 ["--module", "--merge-abc"], 2856 "merge_mode", 2857 es2abc_versions 2858 ) 2859 abc_tests_prepare.gen_abc_tests( 2860 "version_control/bytecode_version_control/mixed_compile", 2861 "js", 2862 ["--module", "--merge-abc"], 2863 "merge_mode", 2864 ) 2865 2866 args.abc_tests_prepare = abc_tests_prepare 2867 abc_version_control_runner = AbcVersionControlRunner(args) 2868 abc_version_control_runner.add_directory( 2869 "version_control/bytecode_version_control/non_merge_mode", 2870 "abc", 2871 ["--module", "--enable-abc-input"], 2872 "non_merge_mode", 2873 ) 2874 abc_version_control_runner.add_directory( 2875 "version_control/bytecode_version_control/merge_mode", 2876 "abc", 2877 ["--module", "--enable-abc-input", "--merge-abc"], 2878 "merge_mode", 2879 ) 2880 abc_version_control_runner.add_directory( 2881 "version_control/bytecode_version_control/mixed_compile", 2882 "txt", 2883 ["--module", "--enable-abc-input", "--merge-abc"], 2884 "mix_compile_mode", 2885 ) 2886 runners.append(abc_version_control_runner) 2887 2888 es2abc_version_control_runner = Es2abcVersionControlRunner(args) 2889 es2abc_version_control_runner.add_directory( 2890 "version_control/bytecode_version_control/non_merge_mode", 2891 "abc", 2892 ["--module", "--enable-abc-input"], 2893 "non_merge_mode", 2894 ) 2895 es2abc_version_control_runner.add_directory( 2896 "version_control/bytecode_version_control/merge_mode", 2897 "abc", 2898 ["--module", "--enable-abc-input", "--merge-abc"], 2899 "merge_mode", 2900 ) 2901 runners.append(es2abc_version_control_runner) 2902 2903def add_directory_for_regression(runners, args): 2904 runner = RegressionRunner(args) 2905 runner.add_directory("parser/concurrent", "js", ["--module", "--dump-ast"]) 2906 runner.add_directory("parser/js", "js", ["--parse-only", "--dump-ast"]) 2907 runner.add_directory("parser/script", "ts", ["--parse-only", "--dump-ast"]) 2908 runner.add_directory("parser/ts", "ts", 2909 ["--parse-only", "--module", "--dump-ast"]) 2910 runner.add_directory("parser/ts/type_checker", "ts", 2911 ["--parse-only", "--enable-type-check", "--module", "--dump-ast"]) 2912 runner.add_directory("parser/ts/cases/declaration", "d.ts", 2913 ["--parse-only", "--module", "--dump-ast"], TSDeclarationTest) 2914 runner.add_directory("parser/commonjs", "js", ["--commonjs", "--parse-only", "--dump-ast"]) 2915 runner.add_directory("parser/binder", "js", ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"]) 2916 runner.add_directory("parser/binder", "ts", ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"]) 2917 runner.add_directory("parser/binder/noModule", "ts", ["--dump-assembly", "--dump-literal-buffer", "--target-api-sub-version=beta3"]) 2918 runner.add_directory("parser/binder/api12beta2", "js", ["--dump-assembly", "--target-api-version=12", "--target-api-sub-version=beta2"]) 2919 runner.add_directory("parser/binder/debugInfo", "ts", ["--dump-assembly", "--dump-literal-buffer", "--debug-info", "--module"]) 2920 runner.add_directory("parser/js/emptySource", "js", ["--dump-assembly"]) 2921 runner.add_directory("parser/js/language/arguments-object", "js", ["--parse-only"]) 2922 runner.add_directory("parser/js/language/statements/for-statement", "js", ["--parse-only", "--dump-ast"]) 2923 runner.add_directory("parser/js/language/expressions/optional-chain", "js", ["--parse-only", "--dump-ast"]) 2924 runner.add_directory("parser/js/language/import/syntax/api18", "js", 2925 ["--parse-only", "--module", "--target-api-version=18"]) 2926 runner.add_directory("parser/js/language/import/syntax/api12/beta3", "js", 2927 ["--parse-only", "--module", "--target-api-version=12", "--target-api-sub-version=beta3"]) 2928 runner.add_directory("parser/js/language/import/syntax/api12/beta2", "js", 2929 ["--parse-only", "--module", "--target-api-version=12", "--target-api-sub-version=beta2"]) 2930 runner.add_directory("parser/js/language/import", "ts", 2931 ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-version=12", 2932 "--target-api-sub-version=beta3"]) 2933 runner.add_directory("parser/sendable_class", "ts", 2934 ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"]) 2935 runner.add_directory("parser/sendable_class/api12beta2", "ts", 2936 ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-version=12", "--target-api-sub-version=beta2"]) 2937 runner.add_directory("parser/unicode", "js", ["--parse-only"]) 2938 runner.add_directory("parser/ts/stack_overflow", "ts", ["--parse-only", "--dump-ast"]) 2939 runner.add_directory("parser/js/module-record/module-record-field-name-option.js", "js", 2940 ["--module-record-field-name=abc", "--source-file=abc", "--module", "--dump-normalized-asm-program"]) 2941 runner.add_directory("parser/annotations", "ts", ["--module", "--dump-ast", "--enable-annotations"]) 2942 runner.add_directory("parser/ts/inline-property", "ts", ["--dump-assembly", "--module"]) 2943 2944 runners.append(runner) 2945 2946 transformer_runner = TransformerRunner(args) 2947 transformer_runner.add_directory("parser/ts/transformed_cases", "ts", 2948 ["--parse-only", "--module", "--dump-transformed-ast", 2949 "--check-transformed-ast-structure"]) 2950 2951 runners.append(transformer_runner) 2952 2953 bc_version_runner = BcVersionRunner(args) 2954 bc_version_runner.add_cmd() 2955 2956 runners.append(bc_version_runner) 2957 2958 transformer_api_version_10_runner = TransformerInTargetApiVersion10Runner(args) 2959 transformer_api_version_10_runner.add_directory("parser/ts/transformed_cases_api_version_10", "ts", 2960 ["--parse-only", "--module", "--target-api-version=10", 2961 "--dump-transformed-ast"]) 2962 2963 runners.append(transformer_api_version_10_runner) 2964 2965def add_directory_for_asm(runners, args, mode=""): 2966 runner = AbcToAsmRunner(args, True if mode == "debug" else False) 2967 runner.add_directory("abc2asm/js", "js", []) 2968 runner.add_directory("abc2asm/ts", "ts", []) 2969 runner.add_directory("compiler/js", "js", []) 2970 runner.add_directory("compiler/ts/cases/compiler", "ts", []) 2971 runner.add_directory("compiler/ts/projects", "ts", ["--module"]) 2972 runner.add_directory("compiler/ts/projects", "ts", ["--module", "--merge-abc"]) 2973 runner.add_directory("compiler/dts", "d.ts", ["--module", "--opt-level=0"]) 2974 runner.add_directory("compiler/commonjs", "js", ["--commonjs"]) 2975 runner.add_directory("parser/concurrent", "js", ["--module"]) 2976 runner.add_directory("parser/js", "js", []) 2977 runner.add_directory("parser/script", "ts", []) 2978 runner.add_directory("parser/ts", "ts", ["--module"]) 2979 runner.add_directory("parser/ts/type_checker", "ts", ["--enable-type-check", "--module"]) 2980 runner.add_directory("parser/commonjs", "js", ["--commonjs"]) 2981 runner.add_directory("parser/binder", "js", ["--dump-assembly", "--dump-literal-buffer", "--module"]) 2982 runner.add_directory("parser/binder", "ts", ["--dump-assembly", "--dump-literal-buffer", "--module"]) 2983 runner.add_directory("parser/binder/noModule", "ts", ["--dump-assembly", "--dump-literal-buffer"]) 2984 runner.add_directory("parser/js/emptySource", "js", []) 2985 runner.add_directory("parser/js/language/arguments-object", "js", []) 2986 runner.add_directory("parser/js/language/statements/for-statement", "js", []) 2987 runner.add_directory("parser/js/language/expressions/optional-chain", "js", []) 2988 runner.add_directory("parser/sendable_class", "ts", ["--module"]) 2989 runner.add_directory("parser/unicode", "js", []) 2990 runner.add_directory("parser/ts/stack_overflow", "ts", []) 2991 2992 runners.append(runner) 2993 2994 2995def add_directory_for_compiler(runners, args): 2996 runner = CompilerRunner(args) 2997 compiler_test_infos = [] 2998 compiler_test_infos.append(CompilerTestInfo("compiler/crashStack/enableColumn/js", "js", ["--enable-release-column"])) 2999 compiler_test_infos.append(CompilerTestInfo("compiler/crashStack/enableColumn/ts", "ts", ["--enable-release-column"])) 3000 compiler_test_infos.append(CompilerTestInfo("compiler/crashStack/offColumn/js", "js", [])) 3001 compiler_test_infos.append(CompilerTestInfo("compiler/crashStack/offColumn/ts", "ts", [])) 3002 compiler_test_infos.append(CompilerTestInfo("compiler/js", "js", ["--module"])) 3003 compiler_test_infos.append(CompilerTestInfo("compiler/ts/cases", "ts", [])) 3004 compiler_test_infos.append(CompilerTestInfo("compiler/ts/projects", "ts", ["--module"])) 3005 compiler_test_infos.append(CompilerTestInfo("compiler/ts/projects", "ts", ["--module", "--merge-abc"])) 3006 compiler_test_infos.append(CompilerTestInfo("compiler/annotations-projects", "ts", ["--module", "--enable-annotations", "--merge-abc"])) 3007 compiler_test_infos.append(CompilerTestInfo("compiler/dts", "d.ts", ["--module", "--opt-level=0"])) 3008 compiler_test_infos.append(CompilerTestInfo("compiler/commonjs", "js", ["--commonjs"])) 3009 compiler_test_infos.append(CompilerTestInfo("compiler/interpreter/lexicalEnv", "js", [])) 3010 compiler_test_infos.append(CompilerTestInfo("compiler/sendable", "ts", ["--module", "--target-api-sub-version=beta3"])) 3011 compiler_test_infos.append(CompilerTestInfo("optimizer/js/branch-elimination", "js", 3012 ["--module", "--branch-elimination", "--dump-assembly"])) 3013 compiler_test_infos.append(CompilerTestInfo("optimizer/js/opt-try-catch-func", "js", 3014 ["--module", "--dump-assembly"])) 3015 compiler_test_infos.append(CompilerTestInfo("optimizer/js/unused-inst-opt", "js", 3016 ["--module", "--dump-assembly"])) 3017 compiler_test_infos.append(CompilerTestInfo("compiler/debugInfo/", "js", 3018 ["--debug-info", "--dump-debug-info", "--source-file", "debug-info.js"])) 3019 compiler_test_infos.append(CompilerTestInfo("compiler/js/module-record-field-name-option.js", "js", 3020 ["--module", "--module-record-field-name=abc"])) 3021 compiler_test_infos.append(CompilerTestInfo("compiler/annotations", "ts", ["--module", "--enable-annotations"])) 3022 compiler_test_infos.append(CompilerTestInfo("compiler/generateCache-projects", "ts", 3023 ["--merge-abc", "--file-threads=0", "--cache-file"])) 3024 # Following directories of test cases are for dump-assembly comparison only, and is not executed. 3025 # Check CompilerProjectTest for more details. 3026 compiler_test_infos.append(CompilerTestInfo("optimizer/ts/branch-elimination/projects", "ts", 3027 ["--module", "--branch-elimination", "--merge-abc", "--dump-assembly", 3028 "--file-threads=8"])) 3029 compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/projects", "ts", 3030 ["--merge-abc", "--dump-assembly", "--enable-abc-input", 3031 "--dump-deps-info", "--remove-redundant-file", "--enable-annotations", 3032 "--dump-literal-buffer", "--dump-string", "--abc-class-threads=4"])) 3033 compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/js/projects", "js", 3034 ["--merge-abc", "--dump-assembly", "--enable-abc-input", 3035 "--dump-deps-info", "--remove-redundant-file", 3036 "--dump-literal-buffer", "--dump-string", "--abc-class-threads=4"])) 3037 compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/merge_abc_consistence_check/projects", "js", 3038 ["--merge-abc", "--dump-assembly", "--enable-abc-input", 3039 "--abc-class-threads=4"])) 3040 compiler_test_infos.append(CompilerTestInfo("compiler/cache_projects", "ts", 3041 ["--merge-abc", "--dump-assembly", "--enable-abc-input", 3042 "--dump-deps-info", "--remove-redundant-file", "--enable-annotations", 3043 "--dump-literal-buffer", "--dump-string", "--abc-class-threads=4", 3044 "--cache-file"])) 3045 3046 compiler_test_infos.append(CompilerTestInfo("compiler/ts/shared_module/projects", "ts", 3047 ["--module", "--merge-abc", "--dump-assembly"])) 3048 compiler_test_infos.append(CompilerTestInfo("compiler/protobin", "ts", [])) 3049 compiler_test_infos.append(CompilerTestInfo("compiler/merge_hap/projects", "ts", 3050 ["--merge-abc", "--dump-assembly", "--enable-abc-input", 3051 "--dump-literal-buffer", "--dump-string", "--abc-class-threads=4"])) 3052 compiler_test_infos.append(CompilerTestInfo("compiler/abc2program", "ts", 3053 ["--merge-abc", "--module", "--dump-assembly", "--enable-abc-input", 3054 "--dump-literal-buffer", "--dump-string", "--source-file=source.ts", 3055 "--module-record-field-name=source"])) 3056 3057 if args.enable_arkguard: 3058 prepare_for_obfuscation(compiler_test_infos, runner.test_root) 3059 3060 for info in compiler_test_infos: 3061 runner.add_directory(info.directory, info.extension, info.flags) 3062 3063 filesinfo_compiler_infos = [] 3064 filesinfo_runner = FilesInfoRunner(args) 3065 filesinfo_compiler_infos.append(CompilerTestInfo("compiler/filesInfoTest/sourceLang", "txt", 3066 ["--module", "--merge-abc", "--dump-assembly"])) 3067 filesinfo_compiler_infos.append(CompilerTestInfo("compiler/filesInfoTest/long_path_filesinfo", "txt", 3068 ["--module", "--merge-abc"])) 3069 3070 for info in filesinfo_compiler_infos: 3071 filesinfo_runner.add_directory(info.directory, info.extension, info.flags) 3072 3073 runners.append(runner) 3074 runners.append(filesinfo_runner) 3075 3076 3077def add_directory_for_bytecode(runners, args): 3078 runner = BytecodeRunner(args) 3079 runner.add_directory("bytecode/commonjs", "js", ["--commonjs", "--dump-assembly"]) 3080 runner.add_directory("bytecode/js", "js", ["--dump-assembly"]) 3081 runner.add_directory("bytecode/ts/cases", "ts", ["--dump-assembly"]) 3082 runner.add_directory("bytecode/ts/ic", "ts", ["--dump-assembly"]) 3083 runner.add_directory("bytecode/ts/api11", "ts", ["--dump-assembly", "--module", "--target-api-version=11"]) 3084 runner.add_directory("bytecode/ts/api12", "ts", ["--dump-assembly", "--module", "--target-api-version=12"]) 3085 runner.add_directory("bytecode/ts/api18", "ts", ["--dump-assembly", "--module", "--target-api-version=18"]) 3086 runner.add_directory("bytecode/watch-expression", "js", ["--debugger-evaluate-expression", "--dump-assembly"]) 3087 3088 runners.append(runner) 3089 3090 3091def add_directory_for_debug(runners, args): 3092 runner = RegressionRunner(args) 3093 runner.add_directory("debug/parser", "js", ["--parse-only", "--dump-ast"]) 3094 3095 runners.append(runner) 3096 3097 3098def add_cmd_for_aop_transform(runners, args): 3099 runner = AopTransform(args) 3100 3101 aop_file_path = path.join(runner.test_root, "aop") 3102 lib_suffix = '.so' 3103 #cpp src, deal type, result compare str, abc compare str 3104 msg_list = [ 3105 ["correct_modify.cpp", "compile", "aop_transform_start", "new_abc_content"], 3106 ["correct_no_modify.cpp", "compile", "aop_transform_start", ""], 3107 ["exec_error.cpp", "compile", "Transform exec fail", ""], 3108 ["no_func_transform.cpp", "compile", "os::library_loader::ResolveSymbol get func Transform error", ""], 3109 ["error_format.cpp", "copy_lib", "os::library_loader::Load error", ""], 3110 ["".join(["no_exist", lib_suffix]), "dirct_use", "Failed to find file", ""], 3111 ["error_suffix.xxx", "direct_use", "aop transform file suffix support", ""] 3112 ] 3113 for msg in msg_list: 3114 cpp_file = path.join(aop_file_path, msg[0]) 3115 if msg[1] == 'compile': 3116 lib_file = cpp_file.replace('.cpp', lib_suffix) 3117 remove_file = lib_file 3118 runner.add_cmd(["g++", "--share", "-o", lib_file, cpp_file], "", "", "") 3119 elif msg[1] == 'copy_lib': 3120 lib_file = cpp_file.replace('.cpp', lib_suffix) 3121 remove_file = lib_file 3122 if not os.path.exists(lib_file): 3123 with open(cpp_file, "r") as source_file: 3124 fd = os.open(lib_file, os.O_RDWR | os.O_CREAT | os.O_TRUNC) 3125 target_file = os.fdopen(fd, 'w') 3126 target_file.write(source_file.read()) 3127 elif msg[1] == 'direct_use': 3128 lib_file = cpp_file 3129 remove_file = "" 3130 3131 js_file = path.join(aop_file_path, "test_aop.js") 3132 runner.add_cmd([runner.es2panda, "--merge-abc", "--transform-lib", lib_file, js_file], msg[2], msg[3], remove_file) 3133 3134 runners.append(runner) 3135 3136 3137class AopTransform(Runner): 3138 def __init__(self, args): 3139 Runner.__init__(self, args, "AopTransform") 3140 3141 def add_cmd(self, cmd, compare_str, compare_abc_str, remove_file, func=TestAop): 3142 self.tests += [func(cmd, compare_str, compare_abc_str, remove_file)] 3143 3144 def test_path(self, src): 3145 return src 3146 3147 3148def main(): 3149 args = get_args() 3150 3151 runners = [] 3152 3153 if args.regression: 3154 add_directory_for_regression(runners, args) 3155 3156 if args.abc_to_asm: 3157 add_directory_for_asm(runners, args) 3158 add_directory_for_asm(runners, args, "debug") 3159 3160 if args.tsc: 3161 runners.append(TSCRunner(args)) 3162 3163 if args.compiler: 3164 add_directory_for_compiler(runners, args) 3165 3166 if args.hotfix: 3167 runners.append(HotfixRunner(args)) 3168 3169 if args.hotreload: 3170 runners.append(HotreloadRunner(args)) 3171 3172 if args.coldfix: 3173 runners.append(ColdfixRunner(args)) 3174 3175 if args.coldreload: 3176 runners.append(ColdreloadRunner(args)) 3177 3178 if args.debugger: 3179 runners.append(DebuggerRunner(args)) 3180 3181 if args.base64: 3182 runners.append(Base64Runner(args)) 3183 3184 if args.bytecode: 3185 add_directory_for_bytecode(runners, args) 3186 3187 if args.aop_transform: 3188 add_cmd_for_aop_transform(runners, args) 3189 3190 if args.debug: 3191 add_directory_for_debug(runners, args) 3192 3193 if args.version_control: 3194 add_directory_for_version_control(runners, args) 3195 3196 failed_tests = 0 3197 3198 for runner in runners: 3199 runner.run() 3200 failed_tests += runner.summarize() 3201 3202 if failed_tests > 0: 3203 exit(1) 3204 exit(0) 3205 3206 3207if __name__ == "__main__": 3208 main() 3209