1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Copyright (c) 2021-2022 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, MIN_SUPPORT_BC_VERSION, MIX_COMPILE_ENTRY_POINT 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 else: 601 glob_expression = path.join( 602 self.test_root, directory, "**/*.%s" % (extension)) 603 files = glob(glob_expression, recursive=True) 604 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 605 self.tests += list(map(lambda f: CompilerTest(f, flags), files)) 606 607 def test_path(self, src): 608 return src 609 610 611class CompilerTest(Test): 612 def __init__(self, test_path, flags): 613 Test.__init__(self, test_path, flags) 614 615 def execute_arkguard(self, runner): 616 input_file_path = self.path 617 arkguard_root_dir = os.path.join(runner.test_root, "../../arkguard") 618 arkgurad_entry_path = os.path.join(arkguard_root_dir, "lib/cli/SecHarmony.js") 619 config_path = os.path.join(arkguard_root_dir, "test/compilerTestConfig.json") 620 arkguard_cmd = [ 621 'node', 622 '--no-warnings', 623 arkgurad_entry_path, 624 input_file_path, 625 '--config-path', 626 config_path, 627 '--inplace' 628 ] 629 self.log_cmd(arkguard_cmd) 630 process = subprocess.Popen(arkguard_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 631 out, err = process.communicate() 632 process.wait() 633 success = True 634 if err or process.returncode != 0: 635 success = False 636 self.passed = False 637 self.error = err.decode("utf-8", errors="ignore") 638 return success 639 640 def run(self, runner): 641 test_abc_name = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_") 642 test_abc_path = path.join(runner.build_dir, test_abc_name) 643 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 644 es2abc_cmd.extend(self.flags) 645 es2abc_cmd.extend(["--output=" + test_abc_path]) 646 es2abc_cmd.append(self.path) 647 enable_arkguard = runner.args.enable_arkguard 648 if enable_arkguard: 649 success = self.execute_arkguard(runner) 650 if not success: 651 return self 652 653 process = run_subprocess_with_beta3(self, es2abc_cmd) 654 out, err = process.communicate() 655 if "--dump-assembly" in self.flags: 656 pa_expected_path = "".join([self.get_path_to_expected()[:self.get_path_to_expected().rfind(".txt")], 657 ".pa.txt"]) 658 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 659 try: 660 with open(pa_expected_path, 'r') as fp: 661 expected = fp.read() 662 self.passed = expected == self.output and process.returncode in [0, 1] 663 except Exception: 664 self.passed = False 665 if not self.passed: 666 self.error = err.decode("utf-8", errors="ignore") 667 if os.path.exists(test_abc_path): 668 os.remove(test_abc_path) 669 return self 670 if "--dump-debug-info" in self.flags: 671 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 672 try: 673 with open(self.get_path_to_expected(), 'r') as fp: 674 expected = fp.read() 675 self.passed = expected == self.output and process.returncode in [0, 1] 676 if os.path.exists(test_abc_path): 677 os.remove(test_abc_path) 678 return self 679 except Exception: 680 self.passed = False 681 if not self.passed: 682 self.error = err.decode("utf-8", errors="ignore") 683 if os.path.exists(test_abc_path): 684 os.remove(test_abc_path) 685 return self 686 if err: 687 self.passed = False 688 self.error = err.decode("utf-8", errors="ignore") 689 return self 690 691 ld_library_path = runner.ld_library_path 692 os.environ.setdefault("LD_LIBRARY_PATH", ld_library_path) 693 run_abc_cmd = [runner.ark_js_vm, '--enable-force-gc=false', test_abc_path] 694 self.log_cmd(run_abc_cmd) 695 696 process = subprocess.Popen(run_abc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 697 out, err = process.communicate() 698 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 699 expected_path = self.get_path_to_expected() 700 try: 701 with open(expected_path, 'r') as fp: 702 expected = fp.read() 703 self.passed = expected == self.output and process.returncode in [0, 1] 704 except Exception: 705 self.passed = False 706 707 if not self.passed: 708 self.error = err.decode("utf-8", errors="ignore") 709 710 os.remove(test_abc_path) 711 712 return self 713 714 715class CompilerProjectTest(Test): 716 def __init__(self, projects_path, project, test_paths, flags): 717 Test.__init__(self, "", flags) 718 self.projects_path = projects_path 719 self.project = project 720 self.test_paths = test_paths 721 self.files_info_path = os.path.join(os.path.join(self.projects_path, self.project), 'filesInfo.txt') 722 # Skip execution if --dump-assembly exists in flags 723 self.requires_execution = "--dump-assembly" not in self.flags 724 self.file_record_mapping = None 725 self.generated_abc_inputs_path = os.path.join(os.path.join(self.projects_path, self.project), "abcinputs_gen") 726 self.abc_input_filenames = None 727 self.record_names_path = os.path.join(os.path.join(self.projects_path, self.project), 'recordnames.txt') 728 self.abc_inputs_path = os.path.join(os.path.join(self.projects_path, self.project), 'abcinputs') 729 730 def remove_project(self, runner): 731 project_path = runner.build_dir + "/" + self.project 732 if path.exists(project_path): 733 shutil.rmtree(project_path) 734 if path.exists(self.files_info_path): 735 os.remove(self.files_info_path) 736 if path.exists(self.generated_abc_inputs_path): 737 shutil.rmtree(self.generated_abc_inputs_path) 738 739 def get_file_absolute_path_and_name(self, runner): 740 sub_path = self.path[len(self.projects_path):] 741 file_relative_path = path.split(sub_path)[0] 742 file_name = path.split(sub_path)[1] 743 file_absolute_path = runner.build_dir + "/" + file_relative_path 744 return [file_absolute_path, file_name] 745 746 def gen_single_abc(self, runner): 747 for test_path in self.test_paths: 748 self.path = test_path 749 [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner) 750 if not path.exists(file_absolute_path): 751 os.makedirs(file_absolute_path) 752 753 test_abc_name = ("%s.abc" % (path.splitext(file_name)[0])) 754 test_abc_path = path.join(file_absolute_path, test_abc_name) 755 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 756 es2abc_cmd.extend(self.flags) 757 es2abc_cmd.extend(['%s%s' % ("--output=", test_abc_path)]) 758 es2abc_cmd.append(self.path) 759 process = run_subprocess_with_beta3(self, es2abc_cmd) 760 out, err = process.communicate() 761 if err: 762 self.passed = False 763 self.error = err.decode("utf-8", errors="ignore") 764 self.remove_project(runner) 765 return self 766 767 def collect_record_mapping(self): 768 # Collect record mappings from recordnames.txt, file format: 769 # 'source_file_name:record_name\n' * n 770 if path.exists(self.record_names_path): 771 with open(self.record_names_path) as mapping_fp: 772 mapping_lines = mapping_fp.readlines() 773 self.file_record_mapping = {} 774 for mapping_line in mapping_lines: 775 cur_mapping = mapping_line[:-1].split(":") 776 self.file_record_mapping[cur_mapping[0]] = cur_mapping[1] 777 778 def get_record_name(self, test_path): 779 record_name = os.path.relpath(test_path, os.path.dirname(self.files_info_path)).split('.')[0] 780 if (self.file_record_mapping is not None and record_name in self.file_record_mapping): 781 record_name = self.file_record_mapping[record_name] 782 return record_name 783 784 def collect_abc_inputs(self, runner): 785 # Collect abc input information from the 'abcinputs' directory. Each txt file in the directory 786 # will generate a merged abc file with the same filename and serve as the final abc input. 787 # file format: 'source_file_name.ts\n' * n 788 if not path.exists(self.abc_inputs_path): 789 return 790 if not path.exists(self.generated_abc_inputs_path): 791 os.makedirs(self.generated_abc_inputs_path) 792 self.abc_input_filenames = {} 793 filenames = os.listdir(self.abc_inputs_path) 794 for filename in filenames: 795 if not filename.endswith('.txt'): 796 self.remove_project(runner) 797 raise Exception("Invalid abc input file: %s, only txt files are allowed in abcinputs directory: %s" 798 % (filename, self.abc_inputs_path)) 799 with open(path.join(self.abc_inputs_path, filename)) as abc_inputs_fp: 800 abc_inputs_lines = abc_inputs_fp.readlines() 801 for abc_input_line in abc_inputs_lines: 802 # filename is 'xxx.txt', remove '.txt' here 803 self.abc_input_filenames[abc_input_line[:-1]] = filename[:-len('.txt')] 804 805 def get_belonging_abc_input(self, test_path): 806 filename = os.path.relpath(test_path, os.path.dirname(self.files_info_path)) 807 if (self.abc_input_filenames is not None and filename in self.abc_input_filenames): 808 return self.abc_input_filenames[filename] 809 return None 810 811 def gen_abc_input_files_infos(self, runner, abc_files_infos, final_file_info_f): 812 for abc_files_info_name in abc_files_infos: 813 abc_files_info = abc_files_infos[abc_files_info_name] 814 if len(abc_files_info) != 0: 815 abc_input_path = path.join(self.generated_abc_inputs_path, abc_files_info_name) 816 abc_files_info_path = ("%s-filesInfo.txt" % (abc_input_path)) 817 abc_files_info_fd = os.open(abc_files_info_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC) 818 abc_files_info_f = os.fdopen(abc_files_info_fd, 'w') 819 abc_files_info_f.writelines(abc_files_info) 820 final_file_info_f.writelines('%s-abcinput.abc;;;;%s;\n' % (abc_input_path, abc_files_info_name)) 821 822 def gen_files_info(self, runner): 823 # After collect_record_mapping, self.file_record_mapping stores {'source file name' : 'source file record name'} 824 self.collect_record_mapping() 825 # After collect_abc_inputs, self.abc_input_filenames stores {'source file name' : 'belonging abc input name'} 826 self.collect_abc_inputs(runner) 827 828 fd = os.open(self.files_info_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC) 829 f = os.fdopen(fd, 'w') 830 abc_files_infos = {} 831 for test_path in self.test_paths: 832 record_name = self.get_record_name(test_path) 833 module_kind = 'esm' 834 if (os.path.basename(test_path).startswith("commonjs")): 835 module_kind = 'commonjs' 836 is_shared_module = 'false' 837 if (os.path.basename(test_path).startswith("sharedmodule")): 838 is_shared_module = 'true' 839 file_info = ('%s;%s;%s;%s;%s;%s\n' % (test_path, record_name, module_kind, 840 os.path.relpath(test_path, self.projects_path), record_name, 841 is_shared_module)) 842 belonging_abc_input = self.get_belonging_abc_input(test_path) 843 if belonging_abc_input is not None: 844 if not belonging_abc_input in abc_files_infos: 845 abc_files_infos[belonging_abc_input] = [] 846 abc_files_infos[belonging_abc_input].append(file_info) 847 else: 848 f.writelines(file_info) 849 self.gen_abc_input_files_infos(runner, abc_files_infos, f) 850 f.close() 851 852 def gen_es2abc_cmd(self, runner, input_file, output_file): 853 es2abc_cmd = runner.cmd_prefix + [runner.es2panda] 854 es2abc_cmd.extend(self.flags) 855 es2abc_cmd.extend(['%s%s' % ("--output=", output_file)]) 856 es2abc_cmd.append(input_file) 857 return es2abc_cmd 858 859 def gen_merged_abc_for_abc_input(self, runner, files_info_name): 860 self.passed = True 861 if not files_info_name.endswith(".txt"): 862 return 863 abc_input_files_info_path = path.join(self.generated_abc_inputs_path, files_info_name) 864 abc_input_merged_abc_path = path.join(self.generated_abc_inputs_path, 865 '%s-abcinput.abc' % (files_info_name[:-len('-filesInfo.txt')])) 866 867 abc_input_file_path = '@' + abc_input_files_info_path 868 if "unmerged_abc_input" in self.generated_abc_inputs_path: 869 self.flags.remove("--merge-abc") 870 with open(abc_input_files_info_path, 'r') as fp: 871 abc_input_file_path = fp.read().split(';')[0] 872 873 es2abc_cmd = self.gen_es2abc_cmd(runner, abc_input_file_path, abc_input_merged_abc_path) 874 process = run_subprocess_with_beta3(self, es2abc_cmd) 875 out, err = process.communicate() 876 if err: 877 self.passed = False 878 self.error = err.decode("utf-8", errors="ignore") 879 880 def gen_merged_abc(self, runner): 881 # Generate abc inputs 882 if (os.path.exists(self.generated_abc_inputs_path)): 883 files_info_names = os.listdir(self.generated_abc_inputs_path) 884 for filename in files_info_names: 885 self.gen_merged_abc_for_abc_input(runner, filename) 886 if (not self.passed): 887 self.remove_project(runner) 888 return self 889 # Generate the abc to be tested 890 for test_path in self.test_paths: 891 self.path = test_path 892 if (self.path.endswith("-exec.ts")) or (self.path.endswith("-exec.js")): 893 exec_file_path = self.path 894 [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner) 895 if not path.exists(file_absolute_path): 896 os.makedirs(file_absolute_path) 897 test_abc_name = ("%s.abc" % (path.splitext(file_name)[0])) 898 output_abc_name = path.join(file_absolute_path, test_abc_name) 899 900 # reverse merge-abc flag 901 if "merge_abc_consistence_check" in self.path: 902 if "--merge-abc" in self.flags: 903 self.flags.remove("--merge-abc") 904 else: 905 self.flags.append("--merge-abc") 906 907 es2abc_cmd = self.gen_es2abc_cmd(runner, '@' + self.files_info_path, output_abc_name) 908 compile_context_info_path = path.join(path.join(self.projects_path, self.project), "compileContextInfo.json") 909 if path.exists(compile_context_info_path): 910 es2abc_cmd.append("%s%s" % ("--compile-context-info=", compile_context_info_path)) 911 process = run_subprocess_with_beta3(self, es2abc_cmd) 912 self.path = exec_file_path 913 out, err = process.communicate() 914 915 # restore merge-abc flag 916 if "merge_abc_consistence_check" in self.path and "--merge-abc" not in self.flags: 917 self.flags.append("--merge-abc") 918 919 # Check dump-assembly outputs when required 920 if "--dump-assembly" in self.flags: 921 pa_expected_path = "".join([self.get_path_to_expected()[:self.get_path_to_expected().rfind(".txt")], 922 ".pa.txt"]) 923 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 924 if "merge_abc_consistence_check" in self.path: 925 self.output = self.output.split('.')[0] 926 try: 927 with open(pa_expected_path, 'r') as fp: 928 expected = fp.read() 929 self.passed = expected == self.output and process.returncode in [0, 1] 930 except Exception: 931 self.passed = False 932 if not self.passed: 933 self.error = err.decode("utf-8", errors="ignore") 934 self.remove_project(runner) 935 return self 936 else: 937 return self 938 939 if err: 940 self.passed = False 941 self.error = err.decode("utf-8", errors="ignore") 942 self.remove_project(runner) 943 return self 944 945 def run(self, runner): 946 # Compile all ts source files in the project to abc files. 947 if ("--merge-abc" in self.flags): 948 self.gen_files_info(runner) 949 self.gen_merged_abc(runner) 950 else: 951 self.gen_single_abc(runner) 952 953 if (not self.requires_execution): 954 self.remove_project(runner) 955 return self 956 957 # Run test files that need to be executed in the project. 958 for test_path in self.test_paths: 959 self.path = test_path 960 if self.path.endswith("-exec.ts"): 961 [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner) 962 963 entry_point_name = path.splitext(file_name)[0] 964 test_abc_name = ("%s.abc" % entry_point_name) 965 test_abc_path = path.join(file_absolute_path, test_abc_name) 966 967 ld_library_path = runner.ld_library_path 968 os.environ.setdefault("LD_LIBRARY_PATH", ld_library_path) 969 run_abc_cmd = [runner.ark_js_vm] 970 if ("--merge-abc" in self.flags): 971 run_abc_cmd.extend(["--entry-point", entry_point_name]) 972 run_abc_cmd.extend([test_abc_path]) 973 self.log_cmd(run_abc_cmd) 974 975 process = subprocess.Popen(run_abc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 976 out, err = process.communicate() 977 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 978 expected_path = self.get_path_to_expected() 979 try: 980 with open(expected_path, 'r') as fp: 981 expected = fp.read() 982 self.passed = expected == self.output and process.returncode in [0, 1] 983 except Exception: 984 self.passed = False 985 986 if not self.passed: 987 self.error = err.decode("utf-8", errors="ignore") 988 self.remove_project(runner) 989 return self 990 991 self.passed = True 992 993 self.remove_project(runner) 994 return self 995 996 997class TSDeclarationTest(Test): 998 def get_path_to_expected(self): 999 file_name = self.path[:self.path.find(".d.ts")] 1000 return "%s-expected.txt" % file_name 1001 1002 1003class BcVersionRunner(Runner): 1004 def __init__(self, args): 1005 Runner.__init__(self, args, "Target bc version") 1006 self.ts2abc = path.join(self.test_root, '..', 'scripts', 'ts2abc.js') 1007 1008 def add_cmd(self): 1009 api_sub_version_list = ["beta1", "beta2", "beta3"] 1010 for api_version in range(8, 17): 1011 cmd = self.cmd_prefix + [self.es2panda] 1012 cmd += ["--target-bc-version"] 1013 cmd += ["--target-api-version"] 1014 cmd += [str(api_version)] 1015 self.tests += [BcVersionTest(cmd, api_version)] 1016 node_cmd = ["node"] + [self.ts2abc] 1017 node_cmd += ["".join(["es2abc=", self.es2panda])] 1018 node_cmd += ["--target-api-version"] 1019 node_cmd += [str(api_version)] 1020 self.tests += [BcVersionTest(node_cmd, api_version)] 1021 1022 # Add tests for "--target-api-sub-version" option 1023 if api_version == 12: 1024 for api_sub_version in api_sub_version_list: 1025 new_cmd = cmd.copy() 1026 new_cmd += ["--target-api-sub-version", api_sub_version] 1027 self.tests += [BcVersionTest(new_cmd, str(api_version) + '_' + api_sub_version)] 1028 new_node_cmd = node_cmd.copy() 1029 new_node_cmd += ["--target-api-sub-version", api_sub_version] 1030 self.tests += [BcVersionTest(new_node_cmd, str(api_version) + '_' + api_sub_version)] 1031 1032 def run(self): 1033 for test in self.tests: 1034 test.run() 1035 1036 1037class BcVersionTest(Test): 1038 def __init__(self, cmd, api_version): 1039 Test.__init__(self, "", 0) 1040 self.cmd = cmd 1041 self.api_version = api_version 1042 # To avoid problems when api version is upgraded abruptly, 1043 # the corresponding bytecode version of the api version not written in isa.yaml is alaways the newest version. 1044 self.bc_version_expect = { 1045 8: "13.0.0.0", 1046 9: "9.0.0.0", 1047 10: "9.0.0.0", 1048 11: "11.0.2.0", 1049 12: "12.0.2.0", 1050 "12_beta1": "12.0.2.0", 1051 "12_beta2": "12.0.2.0", 1052 "12_beta3": "12.0.6.0", 1053 13: "12.0.6.0", 1054 14: "12.0.6.0", 1055 15: "13.0.0.0", 1056 16: "13.0.0.0" 1057 } 1058 self.es2abc_script_expect = { 1059 8: "0.0.0.2", 1060 9: "9.0.0.0", 1061 10: "9.0.0.0", 1062 11: "11.0.2.0", 1063 12: "12.0.2.0", 1064 "12_beta1": "12.0.2.0", 1065 "12_beta2": "12.0.2.0", 1066 "12_beta3": "12.0.6.0", 1067 13: "12.0.6.0", 1068 14: "12.0.6.0", 1069 15: "13.0.0.0", 1070 16: "13.0.0.0" 1071 } 1072 1073 def run(self): 1074 process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1075 out, err = process.communicate() 1076 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 1077 if self.cmd[0] == "node": 1078 self.passed = self.es2abc_script_expect.get(self.api_version) == self.output and process.returncode in [0, 1] 1079 else: 1080 self.passed = self.bc_version_expect.get(self.api_version) == self.output and process.returncode in [0, 1] 1081 if not self.passed: 1082 self.error = err.decode("utf-8", errors="ignore") 1083 return self 1084 1085 1086class TransformerRunner(Runner): 1087 def __init__(self, args): 1088 Runner.__init__(self, args, "Transformer") 1089 1090 def add_directory(self, directory, extension, flags): 1091 glob_expression = path.join( 1092 self.test_root, directory, "**/*.%s" % (extension)) 1093 files = glob(glob_expression, recursive=True) 1094 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 1095 1096 self.tests += list(map(lambda f: TransformerTest(f, flags), files)) 1097 1098 def test_path(self, src): 1099 return src 1100 1101 1102class TransformerInTargetApiVersion10Runner(Runner): 1103 def __init__(self, args): 1104 Runner.__init__(self, args, "TransformerInTargetApiVersion10") 1105 1106 def add_directory(self, directory, extension, flags): 1107 glob_expression = path.join( 1108 self.test_root, directory, "**/*.%s" % (extension)) 1109 files = glob(glob_expression, recursive=True) 1110 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 1111 1112 self.tests += list(map(lambda f: TransformerTest(f, flags), files)) 1113 1114 def test_path(self, src): 1115 return src 1116 1117 1118class TransformerTest(Test): 1119 def __init__(self, test_path, flags): 1120 Test.__init__(self, test_path, flags) 1121 1122 def get_path_to_expected(self): 1123 return "%s-transformed-expected.txt" % (path.splitext(self.path)[0]) 1124 1125 def run(self, runner): 1126 cmd = runner.cmd_prefix + [runner.es2panda] 1127 cmd.extend(self.flags) 1128 cmd.append(self.path) 1129 process = run_subprocess_with_beta3(self, cmd) 1130 out, err = process.communicate() 1131 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 1132 1133 expected_path = self.get_path_to_expected() 1134 try: 1135 with open(expected_path, 'r') as fp: 1136 expected = fp.read() 1137 self.passed = expected == self.output and process.returncode in [0, 1] 1138 except Exception: 1139 self.passed = False 1140 1141 if not self.passed: 1142 self.error = err.decode("utf-8", errors="ignore") 1143 1144 return self 1145 1146 1147class PatchTest(Test): 1148 def __init__(self, test_path, mode_arg, target_version, preserve_files): 1149 Test.__init__(self, test_path, "") 1150 self.mode = mode_arg 1151 self.target_version = target_version 1152 self.preserve_files = preserve_files 1153 1154 def gen_cmd(self, runner): 1155 symbol_table_file = os.path.join(self.path, 'base.map') 1156 origin_input_file = 'base.js' 1157 origin_output_abc = os.path.join(self.path, 'base.abc') 1158 modified_input_file = 'base_mod.js' 1159 modified_output_abc = os.path.join(self.path, 'patch.abc') 1160 target_version_cmd = "" 1161 if self.target_version > 0: 1162 target_version_cmd = "--target-api-version=" + str(self.target_version) 1163 1164 gen_base_cmd = runner.cmd_prefix + [runner.es2panda, '--module', target_version_cmd] 1165 if 'record-name-with-dots' in os.path.basename(self.path): 1166 gen_base_cmd.extend(['--merge-abc', '--record-name=record.name.with.dots']) 1167 gen_base_cmd.extend(['--dump-symbol-table', symbol_table_file]) 1168 gen_base_cmd.extend(['--output', origin_output_abc]) 1169 gen_base_cmd.extend([os.path.join(self.path, origin_input_file)]) 1170 self.log_cmd(gen_base_cmd) 1171 1172 if self.mode == 'hotfix': 1173 mode_arg = ["--generate-patch"] 1174 elif self.mode == 'hotreload': 1175 mode_arg = ["--hot-reload"] 1176 elif self.mode == 'coldfix': 1177 mode_arg = ["--generate-patch", "--cold-fix"] 1178 elif self.mode == 'coldreload': 1179 mode_arg = ["--cold-reload"] 1180 1181 patch_test_cmd = runner.cmd_prefix + [runner.es2panda, '--module', target_version_cmd] 1182 patch_test_cmd.extend(mode_arg) 1183 patch_test_cmd.extend(['--input-symbol-table', symbol_table_file]) 1184 patch_test_cmd.extend(['--output', modified_output_abc]) 1185 patch_test_cmd.extend([os.path.join(self.path, modified_input_file)]) 1186 if 'record-name-with-dots' in os.path.basename(self.path): 1187 patch_test_cmd.extend(['--merge-abc', '--record-name=record.name.with.dots']) 1188 dump_assembly_testname = [ 1189 'modify-anon-content-keep-origin-name', 1190 'modify-class-memeber-function', 1191 'exist-lexenv-3', 1192 'lexenv-reduce', 1193 'lexenv-increase'] 1194 for name in dump_assembly_testname: 1195 if name in os.path.basename(self.path): 1196 patch_test_cmd.extend(['--dump-assembly']) 1197 self.log_cmd(patch_test_cmd) 1198 1199 return gen_base_cmd, patch_test_cmd, symbol_table_file, origin_output_abc, modified_output_abc 1200 1201 def run(self, runner): 1202 gen_base_cmd, patch_test_cmd, symbol_table_file, origin_output_abc, modified_output_abc = self.gen_cmd(runner) 1203 1204 process_base = run_subprocess_with_beta3(None, gen_base_cmd) 1205 stdout_base, stderr_base = process_base.communicate(timeout=runner.args.es2panda_timeout) 1206 if stderr_base: 1207 self.passed = False 1208 self.error = stderr_base.decode("utf-8", errors="ignore") 1209 self.output = stdout_base.decode("utf-8", errors="ignore") + stderr_base.decode("utf-8", errors="ignore") 1210 else: 1211 process_patch = run_subprocess_with_beta3(None, patch_test_cmd) 1212 process_patch = subprocess.Popen(patch_test_cmd, stdout=subprocess.PIPE, 1213 stderr=subprocess.PIPE) 1214 stdout_patch, stderr_patch = process_patch.communicate(timeout=runner.args.es2panda_timeout) 1215 if stderr_patch: 1216 self.passed = False 1217 self.error = stderr_patch.decode("utf-8", errors="ignore") 1218 self.output = stdout_patch.decode("utf-8", errors="ignore") + stderr_patch.decode("utf-8", errors="ignore") 1219 1220 expected_path = os.path.join(self.path, 'expected.txt') 1221 try: 1222 with open(expected_path, 'r') as fp: 1223 # ignore license description lines and skip leading blank lines 1224 expected = (''.join((fp.readlines()[12:]))).lstrip() 1225 self.passed = expected == self.output 1226 except Exception: 1227 self.passed = False 1228 1229 if not self.passed: 1230 self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\ 1231 self.output 1232 if not self.preserve_files: 1233 os.remove(symbol_table_file) 1234 os.remove(origin_output_abc) 1235 if (os.path.exists(modified_output_abc)): 1236 os.remove(modified_output_abc) 1237 return self 1238 1239 1240class PatchRunner(Runner): 1241 def __init__(self, args, name): 1242 Runner.__init__(self, args, name) 1243 self.preserve_files = args.error 1244 self.tests_in_dirs = [] 1245 dirs = os.listdir(path.join(self.test_root, "patch")) 1246 for target_version_path in dirs: 1247 self.add_tests(target_version_path, name) 1248 1249 def add_tests(self, target_version_path, name): 1250 name_dir = os.path.join(self.test_root, "patch", target_version_path, name) 1251 if not os.path.exists(name_dir): 1252 return 1253 target_version = 0 1254 if target_version_path.isdigit(): 1255 target_version = int(target_version_path) 1256 for sub_path in os.listdir(name_dir): 1257 test_base_path = os.path.join(name_dir, sub_path) 1258 if name != "coldreload": 1259 for test_dir in os.listdir(test_base_path): 1260 test_path = os.path.join(test_base_path, test_dir) 1261 self.tests_in_dirs.append(test_path) 1262 self.tests.append(PatchTest(test_path, name, target_version, self.preserve_files)) 1263 else: 1264 self.tests_in_dirs.append(test_base_path) 1265 self.tests.append(PatchTest(test_base_path, name, target_version, self.preserve_files)) 1266 1267 def test_path(self, src): 1268 return os.path.basename(src) 1269 1270 1271class HotfixRunner(PatchRunner): 1272 def __init__(self, args): 1273 PatchRunner.__init__(self, args, "hotfix") 1274 1275 1276class HotreloadRunner(PatchRunner): 1277 def __init__(self, args): 1278 PatchRunner.__init__(self, args, "hotreload") 1279 1280 1281class ColdfixRunner(PatchRunner): 1282 def __init__(self, args): 1283 PatchRunner.__init__(self, args, "coldfix") 1284 1285 1286class ColdreloadRunner(PatchRunner): 1287 def __init__(self, args): 1288 PatchRunner.__init__(self, args, "coldreload") 1289 1290 1291class DebuggerTest(Test): 1292 def __init__(self, test_path, mode): 1293 Test.__init__(self, test_path, "") 1294 self.mode = mode 1295 1296 def run(self, runner): 1297 cmd = runner.cmd_prefix + [runner.es2panda, "--module"] 1298 input_file_name = 'base.js' 1299 if self.mode == "debug-mode": 1300 cmd.extend(['--debug-info']) 1301 cmd.extend([os.path.join(self.path, input_file_name)]) 1302 cmd.extend(['--dump-assembly']) 1303 process = run_subprocess_with_beta3(self, cmd) 1304 stdout, stderr = process.communicate(timeout=runner.args.es2panda_timeout) 1305 if stderr: 1306 self.passed = False 1307 self.error = stderr.decode("utf-8", errors="ignore") 1308 return self 1309 1310 self.output = stdout.decode("utf-8", errors="ignore") 1311 1312 expected_path = os.path.join(self.path, 'expected.txt') 1313 try: 1314 with open(expected_path, 'r') as fp: 1315 expected = (''.join((fp.readlines()[12:]))).lstrip() 1316 self.passed = expected == self.output 1317 except Exception: 1318 self.passed = False 1319 1320 if not self.passed: 1321 self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\ 1322 self.output 1323 1324 if os.path.exists("base.abc"): 1325 os.remove("base.abc") 1326 1327 return self 1328 1329 1330class DebuggerRunner(Runner): 1331 def __init__(self, args): 1332 Runner.__init__(self, args, "debugger") 1333 self.test_directory = path.join(self.test_root, "debugger") 1334 self.add_test() 1335 1336 def add_test(self): 1337 self.tests = [] 1338 self.tests.append(DebuggerTest(os.path.join(self.test_directory, "debugger-in-debug"), "debug-mode")) 1339 self.tests.append(DebuggerTest(os.path.join(self.test_directory, "debugger-in-release"), "release-mode")) 1340 1341 1342class Base64Test(Test): 1343 def __init__(self, test_path, input_type): 1344 Test.__init__(self, test_path, "") 1345 self.input_type = input_type 1346 1347 def run(self, runner): 1348 cmd = runner.cmd_prefix + [runner.es2panda, "--base64Output"] 1349 if self.input_type == "file": 1350 input_file_name = 'input.js' 1351 cmd.extend(['--source-file', input_file_name]) 1352 cmd.extend([os.path.join(self.path, input_file_name)]) 1353 elif self.input_type == "string": 1354 input_file = os.path.join(self.path, "input.txt") 1355 try: 1356 with open(input_file, 'r') as fp: 1357 base64_input = (''.join((fp.readlines()[12:]))).lstrip() # ignore license description lines 1358 cmd.extend(["--base64Input", base64_input]) 1359 except Exception: 1360 self.passed = False 1361 elif self.input_type == "targetApiVersion": 1362 # base64 test for all available target api version. 1363 version = os.path.basename(self.path) 1364 cmd.extend(['--target-api-version', version]) 1365 if version == "12": 1366 cmd.append("--target-api-sub-version=beta3") 1367 input_file = os.path.join(self.path, "input.txt") 1368 try: 1369 with open(input_file, 'r') as fp: 1370 base64_input = (''.join((fp.readlines()[12:]))).lstrip() # ignore license description lines 1371 cmd.extend(["--base64Input", base64_input]) 1372 except Exception: 1373 self.passed = False 1374 else: 1375 self.error = "Unsupported base64 input type" 1376 self.passed = False 1377 return self 1378 1379 version = os.path.basename(self.path) 1380 if not self.input_type == "targetApiVersion": 1381 cmd.append("--target-api-sub-version=beta3") 1382 1383 self.log_cmd(cmd) 1384 1385 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1386 stdout, stderr = process.communicate(timeout=runner.args.es2panda_timeout) 1387 if stderr: 1388 self.passed = False 1389 self.error = stderr.decode("utf-8", errors="ignore") 1390 return self 1391 1392 self.output = stdout.decode("utf-8", errors="ignore") 1393 1394 expected_path = os.path.join(self.path, 'expected.txt') 1395 try: 1396 with open(expected_path, 'r') as fp: 1397 expected = (''.join((fp.readlines()[12:]))).lstrip() 1398 self.passed = expected == self.output 1399 except Exception: 1400 self.passed = False 1401 1402 if not self.passed: 1403 self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\ 1404 self.output 1405 1406 return self 1407 1408 1409class Base64Runner(Runner): 1410 def __init__(self, args): 1411 Runner.__init__(self, args, "Base64") 1412 self.test_directory = path.join(self.test_root, "base64") 1413 self.add_test() 1414 1415 def add_test(self): 1416 self.tests = [] 1417 self.tests.append(Base64Test(os.path.join(self.test_directory, "inputFile"), "file")) 1418 self.tests.append(Base64Test(os.path.join(self.test_directory, "inputString"), "string")) 1419 # current target api version is 12, once a new version is addded, a new testcase should be added here. 1420 current_version = 12 1421 available_target_api_versions = [9, 10, 11, current_version] 1422 for version in available_target_api_versions: 1423 self.tests.append(Base64Test(os.path.join(self.test_directory, "availableTargetApiVersion", str(version)), 1424 "targetApiVersion")) 1425 1426 def test_path(self, src): 1427 return os.path.basename(src) 1428 1429 1430class BytecodeRunner(Runner): 1431 def __init__(self, args): 1432 Runner.__init__(self, args, "Bytecode") 1433 1434 def add_directory(self, directory, extension, flags, func=Test): 1435 glob_expression = path.join( 1436 self.test_root, directory, "**/*.%s" % (extension)) 1437 files = glob(glob_expression, recursive=True) 1438 files = fnmatch.filter(files, self.test_root + '**' + self.args.filter) 1439 self.tests += list(map(lambda f: func(f, flags), files)) 1440 1441 def test_path(self, src): 1442 return src 1443 1444 1445class ArkJsVmDownload: # Obtain different versions of ark_js_vm and their dependent libraries 1446 def __init__(self, args): 1447 self.build_dir = args.build_dir 1448 self.url = "https://gitee.com/zhongmingwei123123/ark_js_vm_version.git" 1449 self.local_path = path.join(self.build_dir, "ark_js_vm_version") 1450 self.max_retries = 3 1451 1452 def run_cmd_cwd(self, cmd): 1453 try: 1454 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1455 _, _ = proc.communicate() 1456 return proc.wait() 1457 except Exception as e: 1458 print(f"Error executing command: {e}") 1459 return -1 1460 1461 def git_clone(self, git_url, code_dir): 1462 cmd = ["git", "clone", git_url, code_dir] 1463 retries = 1 1464 while retries <= self.max_retries: 1465 ret = self.run_cmd_cwd(cmd) 1466 if ret == 0: 1467 break 1468 else: 1469 print(f"\n warning: Atempt: #{retries} to clone '{git_url}' failed. Try cloining again") 1470 retries += 1 1471 assert not ret, f"\n error: Cloning '{git_url}' failed." 1472 1473 def run(self): 1474 if not os.path.exists(self.local_path): 1475 print("\nstart downLoad ark_js_vm_version ...\n") 1476 self.git_clone(self.url, self.local_path) 1477 print("\ndownload finish.\n") 1478 1479 1480class AbcTestCasesPrepare: 1481 def __init__(self, args): 1482 self.test_root = path.dirname(path.abspath(__file__)) 1483 self.es2panda = path.join(args.build_dir, "es2abc") 1484 self.args = args 1485 self.valid_mode_list = ["non_merge_mode", "merge_mode"] 1486 self.test_abc_path_list = set() 1487 1488 @staticmethod 1489 def split_api_version(version_str): 1490 parts = version_str.split("API")[1].split("beta") 1491 main_part = parts[0] 1492 beta_part = "beta%s" % parts[1] if len(parts) > 1 else "" 1493 return (main_part, beta_part) 1494 1495 def add_abc_directory(self, directory, extension): 1496 test_directory = path.join(self.test_root, directory) 1497 glob_expression = path.join(test_directory, "*.%s" % (extension)) 1498 files = glob(glob_expression) 1499 files = fnmatch.filter(files, self.test_root + "**" + self.args.filter) 1500 return files 1501 1502 def gen_abc_versions(self, flags, source_path): 1503 for api_version in API_VERSION_MAP: 1504 main_version, beta_version = AbcTestCasesPrepare.split_api_version(api_version) 1505 output_path = "%s_version_API%s%s.abc" % ( 1506 path.splitext(source_path)[0], 1507 main_version, 1508 beta_version, 1509 ) 1510 self.test_abc_path_list.add(output_path) 1511 _, stderr = self.compile_for_target_version(flags, source_path, output_path, main_version, beta_version) 1512 if stderr: 1513 raise RuntimeError(f"abc generate error: " % (stderr.decode("utf-8", errors="ignore"))) 1514 1515 def gen_abc_tests(self, directory, extension, flags, abc_mode): 1516 if abc_mode not in self.valid_mode_list: 1517 raise ValueError(f"Invalid abc_mode value: {abc_mode}") 1518 test_source_list = self.add_abc_directory(directory, extension) 1519 for input_path in test_source_list: 1520 self.gen_abc_versions(flags, input_path) 1521 1522 def compile_for_target_version(self, flags, input_path, output_path, target_api_version, target_api_sub_version=""): 1523 cmd = [] 1524 cmd.append(self.es2panda) 1525 cmd.append(input_path) 1526 cmd.extend(flags) 1527 cmd.append("--target-api-version=%s" % (target_api_version)) 1528 cmd.extend(["--output=%s" % (output_path)]) 1529 if target_api_version != "": 1530 cmd.append("--target-api-sub-version=%s" % (target_api_sub_version)) 1531 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1532 stdout, stderr = process.communicate(timeout=10) 1533 if stderr: 1534 stderr = "Error executing command: %s\n%s" % (cmd, stderr) 1535 return stdout, stderr 1536 1537 def remove_abc_tests(self): 1538 for abc_path in self.test_abc_path_list: 1539 if path.exists(abc_path): 1540 os.remove(abc_path) 1541 1542 1543class AbcVersionControlRunner(Runner): 1544 def __init__(self, args): 1545 super().__init__(args, "AbcVersionControl") 1546 self.valid_mode_list = ["non_merge_mode", "merge_mode", "mix_compile_mode"] 1547 1548 def add_directory(self, directory, extension, flags, abc_mode, is_discard=False): 1549 if abc_mode not in self.valid_mode_list: 1550 raise ValueError(f"Invalid abc_mode value: {abc_mode}") 1551 glob_expression = path.join(self.test_root, directory, "*.%s" % (extension)) 1552 files = glob(glob_expression) 1553 files = fnmatch.filter(files, self.test_root + "**" + self.args.filter) 1554 if abc_mode == "mix_compile_mode": 1555 files = [f for f in files if not f.endswith("-expected.txt")] 1556 self.tests += list(map(lambda f: TestAbcVersionControl(f, flags, abc_mode, is_discard), files)) 1557 1558 def test_path(self, src): 1559 return src 1560 1561 def run(self): 1562 for test in self.tests: 1563 test.run(self) 1564 self.args.abc_tests_prepare.remove_abc_tests() 1565 1566 1567class VersionControlRunner(Runner): 1568 def __init__(self, args): 1569 Runner.__init__(self, args, "VersionControl") 1570 1571 def add_directory(self, directory, extension, flags, test_version, feature_type, module_dir=None, func=Test): 1572 glob_expression = path.join(self.test_root, directory, "*.%s" % (extension)) 1573 files = glob(glob_expression) 1574 files = fnmatch.filter(files, self.test_root + "**" + self.args.filter) 1575 module_path_list = [] 1576 if module_dir is not None: 1577 module_path_list = self.add_module_path(module_dir) 1578 self.tests += list( 1579 map(lambda f: TestVersionControl(f, flags, test_version, feature_type, module_path_list), files) 1580 ) 1581 1582 def add_module_path(self, module_dir): 1583 module_path_list = [] 1584 glob_expression_ts = path.join(self.test_root, module_dir, "*.%s" % ("ts")) 1585 glob_expression_js = path.join(self.test_root, module_dir, "*.%s" % ("js")) 1586 module_path_list = glob(glob_expression_ts) 1587 module_path_list.extend(glob(glob_expression_js)) 1588 module_path_list = fnmatch.filter(module_path_list, self.test_root + "**" + self.args.filter) 1589 return module_path_list 1590 1591 def test_path(self, src): 1592 return src 1593 1594 def run(self): 1595 for test in self.tests: 1596 test.run(self) 1597 1598 1599class TestAbcVersionControl(Test): 1600 def __init__(self, test_path, flags, abc_mode, is_discard): 1601 super().__init__(test_path, flags) 1602 self.min_support_version_number = API_VERSION_MAP.get(MIN_SUPPORT_BC_VERSION) 1603 self.abc_mode = abc_mode 1604 self.is_discard = is_discard 1605 self.output = None 1606 self.process = None 1607 self.is_support = False 1608 self.test_abc_list = list() 1609 self.test_input = None 1610 self.target_abc_path = None 1611 self.entry_point = self.get_entry_point() 1612 1613 @staticmethod 1614 def compare_version_number(version1, version2): 1615 v1 = TestAbcVersionControl.version_number_to_tuple(version1) 1616 v2 = TestAbcVersionControl.version_number_to_tuple(version2) 1617 for num1, num2 in zip(v1, v2): 1618 if num1 > num2: 1619 return 1 1620 elif num1 < num2: 1621 return -1 1622 return 0 1623 1624 @staticmethod 1625 def version_number_to_tuple(version): 1626 return tuple(int(part) for part in version.split(".")) 1627 1628 def get_entry_point(self): 1629 if self.abc_mode == "merge_mode": 1630 base_name = os.path.basename(self.path) 1631 return os.path.splitext(base_name)[0] 1632 elif self.abc_mode == "mix_compile_mode": 1633 return MIX_COMPILE_ENTRY_POINT 1634 return "" 1635 1636 def get_path_to_expected(self, is_support=False, test_stage=""): 1637 support_name = "supported_" if is_support else "unsupported_" 1638 if self.abc_mode == "mix_compile_mode" and test_stage != "runtime": 1639 support_name = "" 1640 expected_name = path.splitext(self.path)[0].split("_version_API")[0] 1641 expected_path = "%s_%s%s-expected.txt" % (expected_name, support_name, test_stage) 1642 return expected_path 1643 1644 def run_process(self, cmd): 1645 self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1646 stdout, stderr = self.process.communicate(timeout=10) 1647 self.output = stdout.decode("utf-8", errors="ignore") + stderr.decode("utf-8", errors="ignore").split("\n")[0] 1648 if stderr: 1649 stderr = "Error executing command: %s\n%s" % (cmd, stderr) 1650 return stdout, stderr 1651 1652 def compile_for_target_version( 1653 self, runner, input_path, output_path, target_api_version, target_api_sub_version="" 1654 ): 1655 cmd = [] 1656 cmd.append(runner.es2panda) 1657 if self.abc_mode == "mix_compile_mode": 1658 input_path = "@%s" % (input_path) 1659 cmd.append(input_path) 1660 cmd.extend(self.flags) 1661 cmd.append("--target-api-version=%s" % (target_api_version)) 1662 cmd.extend(["--output=%s" % (output_path)]) 1663 if target_api_version != "": 1664 cmd.append("--target-api-sub-version=%s" % (target_api_sub_version)) 1665 stdout, stderr = self.run_process(cmd) 1666 return stdout, stderr 1667 1668 def generate_abc(self, runner, target_api_version, target_api_sub_version=""): 1669 compile_expected_path = None 1670 target_abc_name = ( 1671 "%s_target_%s%s.abc" % (path.splitext(self.path)[0], target_api_version, target_api_sub_version) 1672 ).replace("/", "_") 1673 self.target_abc_path = path.join(runner.build_dir, target_abc_name) 1674 _, stderr = self.compile_for_target_version( 1675 runner, self.path, self.target_abc_path, target_api_version, target_api_sub_version 1676 ) 1677 format_content = "" 1678 self.is_support = False 1679 1680 # Extract the API versions of the input abc files from the file name of the test case. 1681 input_api_versions = self.extract_api_versions(path.split(self.path)[1]) 1682 input_version_numbers = [API_VERSION_MAP.get(api) for api in input_api_versions] 1683 sorted(input_version_numbers, key=TestAbcVersionControl.version_number_to_tuple) 1684 min_input_version_number = input_version_numbers[0] 1685 max_input_version_number = input_version_numbers[-1] 1686 target_version = "API" + target_api_version + target_api_sub_version 1687 target_version_number = API_VERSION_MAP.get(target_version) 1688 1689 if TestAbcVersionControl.compare_version_number(target_version_number, self.min_support_version_number) < 0: 1690 compile_expected_path = self.get_path_to_expected( 1691 self.is_support, "compile_target_version_below_min_support" 1692 ) 1693 format_content = target_api_version 1694 elif ( 1695 TestAbcVersionControl.compare_version_number(min_input_version_number, self.min_support_version_number) < 0 1696 ): 1697 compile_expected_path = self.get_path_to_expected(self.is_support, "compile_cur_version_below_min_support") 1698 format_content = self.path 1699 elif TestAbcVersionControl.compare_version_number(target_version_number, max_input_version_number) < 0: 1700 compile_expected_path = self.get_path_to_expected(self.is_support, "compile_target_version_below_cur") 1701 format_content = self.path 1702 elif self.is_discard: 1703 compile_expected_path = self.get_path_to_expected(self.is_support, "compile_discard") 1704 else: 1705 self.is_support = True 1706 if stderr: 1707 self.passed = False 1708 return stderr 1709 1710 try: 1711 with open(compile_expected_path, "r") as fp: 1712 expected = fp.read() 1713 self.passed = expected.format(format_content) in self.output and self.process.returncode in [0, 1] 1714 except Exception: 1715 self.passed = False 1716 return stderr 1717 1718 def execute_abc(self, runner, vm_api_version, vm_api_sub_version="", entry_point=""): 1719 cmd = [] 1720 if vm_api_version != "12": 1721 vm_api_sub_version = "" 1722 # there is no virtual machine with version api12beta2 available. 1723 # chosen api12beta1 as a replacement. 1724 elif vm_api_version == "12" and vm_api_sub_version == "beta2": 1725 vm_api_sub_version = "beta1" 1726 ark_js_vm_dir = os.path.join( 1727 runner.build_dir, 1728 "ark_js_vm_version", 1729 "API%s%s" % (vm_api_version, vm_api_sub_version), 1730 ) 1731 ld_library_path = os.path.join(ark_js_vm_dir, "lib") 1732 os.environ["LD_LIBRARY_PATH"] = ld_library_path 1733 ark_js_vm_path = os.path.join(ark_js_vm_dir, "ark_js_vm") 1734 cmd.append(ark_js_vm_path) 1735 if entry_point != "": 1736 cmd.append("--entry-point=%s" % entry_point) 1737 cmd.append(self.target_abc_path) 1738 stdout, stderr = self.run_process(cmd) 1739 return stdout, stderr 1740 1741 def test_abc_execution(self, runner, target_api_version, target_api_sub_version=""): 1742 stderr = None 1743 target_version = "API" + target_api_version + target_api_sub_version 1744 target_version_number = API_VERSION_MAP.get(target_version) 1745 for api_version in API_VERSION_MAP: 1746 vm_api_version, vm_api_sub_version = AbcTestCasesPrepare.split_api_version(api_version) 1747 vm_version = "API" + vm_api_version + vm_api_sub_version 1748 vm_version_number = API_VERSION_MAP.get(vm_version) 1749 _, stderr = self.execute_abc(runner, vm_api_version, vm_api_sub_version, self.entry_point) 1750 self.is_support = ( 1751 TestAbcVersionControl.compare_version_number(vm_version_number, target_version_number) >= 0 1752 ) 1753 runtime_expect_path = self.get_path_to_expected(self.is_support, "runtime") 1754 try: 1755 with open(runtime_expect_path, "r") as fp: 1756 expected = fp.read() 1757 if self.is_support and self.abc_mode != "merge_mode": 1758 self.passed = expected == self.output and self.process.returncode in [0, 1, 255] 1759 else: 1760 self.passed = expected in self.output 1761 pass 1762 except Exception: 1763 self.passed = False 1764 if not self.passed: 1765 return stderr 1766 return stderr 1767 1768 def extract_api_versions(self, file_name): 1769 pattern = r"(API\d+)(beta\d+)?" 1770 matches = re.findall(pattern, file_name) 1771 api_versions = [f"{api}{f'{beta}' if beta else ''}" for api, beta in matches] 1772 return api_versions 1773 1774 def remove_abc(self, abc_path): 1775 if path.exists(abc_path): 1776 os.remove(abc_path) 1777 1778 def run(self, runner): 1779 for api_version in API_VERSION_MAP: 1780 target_api_version, target_api_sub_version = AbcTestCasesPrepare.split_api_version(api_version) 1781 stderr = self.generate_abc(runner, target_api_version, target_api_sub_version) 1782 if not self.passed: 1783 self.error = stderr.decode("utf-8", errors="ignore") 1784 return self 1785 if stderr: 1786 continue 1787 stderr = self.test_abc_execution(runner, target_api_version, target_api_sub_version) 1788 self.remove_abc(self.target_abc_path) 1789 if not self.passed: 1790 self.error = stderr.decode("utf-8", errors="ignore") 1791 return self 1792 return self 1793 1794 1795class TestVersionControl(Test): 1796 def __init__(self, test_path, flags, test_version, feature_type, module_path_list): 1797 Test.__init__(self, test_path, flags) 1798 self.beta_version_default = 3 1799 self.version_with_sub_version_list = [12] 1800 self.target_api_version_list = ["9", "10", "11", "12"] 1801 self.target_api_sub_version_list = ["beta1", "beta2", "beta3"] 1802 self.specific_api_version_list = ["API11", "API12beta3"] 1803 self.output = None 1804 self.process = None 1805 self.test_version = test_version 1806 self.test_abc_path = None 1807 self.feature_type = feature_type 1808 self.module_path_list = module_path_list 1809 self.module_abc_path_set = set() 1810 1811 def split_version(self, version_str): 1812 parts = version_str.split("API")[1].split("beta") 1813 main_part = int(parts[0]) 1814 beta_part = int(parts[1]) if len(parts) > 1 else self.beta_version_default 1815 return (main_part, beta_part) 1816 1817 def compare_two_versions(self, version1, version2): 1818 version1_parsed = self.split_version(version1) 1819 version2_parsed = self.split_version(version2) 1820 1821 if version1_parsed < version2_parsed: 1822 return -1 1823 elif version1_parsed > version2_parsed: 1824 return 1 1825 else: 1826 return 0 1827 1828 def get_relative_path(self, from_dir, to_dir): 1829 from_dir = os.path.normpath(from_dir) 1830 to_dir = os.path.normpath(to_dir) 1831 from_dir = os.path.abspath(from_dir) 1832 to_dir = os.path.abspath(to_dir) 1833 from_parts = from_dir.split(os.sep) 1834 to_parts = to_dir.split(os.sep) 1835 common_prefix_length = 0 1836 for part1, part2 in zip(from_parts, to_parts): 1837 if part1 == part2: 1838 common_prefix_length += 1 1839 else: 1840 break 1841 relative_parts = [".."] * (len(from_parts) - common_prefix_length) + to_parts[common_prefix_length:] 1842 relative_path = os.path.join(*relative_parts) 1843 return relative_path 1844 1845 def generate_single_module_abc(self, runner, module_path, target_version): 1846 cmd = [] 1847 cmd.append(runner.es2panda) 1848 cmd.append(module_path) 1849 cmd.append("--module") 1850 main_version, sub_version = self.split_version(target_version) 1851 cmd.append("--target-api-version=%s" % (main_version)) 1852 if main_version == 12: 1853 cmd.append("--target-api-sub-version=beta%s" % (sub_version)) 1854 1855 basename = os.path.basename(module_path) 1856 module_abc_name = "%s.abc" % (path.splitext(basename)[0]) 1857 relative_path = self.get_relative_path(path.split(self.path)[0], path.split(module_path)[0]) 1858 module_abc_dir = path.join(runner.build_dir, relative_path) 1859 if not os.path.exists(module_abc_dir): 1860 os.makedirs(module_abc_dir) 1861 module_abc_path = path.join(module_abc_dir, module_abc_name) 1862 self.module_abc_path_set.add(module_abc_path) 1863 cmd.extend(["--output=%s" % (module_abc_path)]) 1864 1865 self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1866 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1867 _, stderr = proc.communicate() 1868 proc.wait() 1869 if stderr: 1870 print(stderr.decode("utf-8", errors="ignore")) 1871 1872 def generate_module_abc(self, runner, target_version): 1873 for module_path in self.module_path_list: 1874 self.generate_single_module_abc(runner, module_path, target_version) 1875 1876 def remove_module_abc(self): 1877 for module_abc_path in self.module_abc_path_set: 1878 if path.exists(module_abc_path): 1879 os.remove(module_abc_path) 1880 1881 def get_path_to_expected( 1882 self, is_support, expected_stage, target_api_version="", specific_api_version="", dump_type="" 1883 ): 1884 support_name = "supported" if is_support else "unsupported" 1885 api_name = "" 1886 # Higher than the specific API version, expected results may differ 1887 if target_api_version != "" and specific_api_version != "": 1888 if self.compare_two_versions(target_api_version, specific_api_version) >= 0: 1889 api_name = "for_higher_or_equal_to_%s_" % (specific_api_version) 1890 else: 1891 api_name = "for_below_%s_" % (specific_api_version) 1892 if dump_type == "ast": 1893 dump_type = "" 1894 elif dump_type == "asm": 1895 dump_type = "asm_" 1896 expected_path = "%s_%s_%s_%s%sversion-expected.txt" % ( 1897 path.splitext(self.path)[0], 1898 support_name, 1899 expected_stage, 1900 api_name, 1901 dump_type, 1902 ) 1903 return expected_path 1904 1905 def get_path_to_runtime_output_below_version_expected(self): 1906 expected_path = "%s_runtime_below_abc_api_version-expected.txt" % ( 1907 path.splitext(self.path)[0]) 1908 return expected_path 1909 1910 def get_path_to_runtime_output_expected(self, is_support, target_api_version, is_below_abc_api_version): 1911 path_expected = None 1912 if is_below_abc_api_version: 1913 path_expected = self.get_path_to_runtime_output_below_version_expected() 1914 return path_expected 1915 for specific_api_version in self.specific_api_version_list: 1916 if self.compare_two_versions(target_api_version, specific_api_version) > 0: 1917 continue 1918 path_expected = self.get_path_to_expected(is_support, "runtime", target_api_version, specific_api_version) 1919 if path.exists(path_expected): 1920 return path_expected 1921 return self.get_path_to_expected(is_support, "runtime", target_api_version) 1922 1923 def get_path_to_compile_ast_output_expected(self, is_support): 1924 return self.get_path_to_expected(is_support, "compile") 1925 1926 def get_path_to_compile_asm_output_expected(self, is_support, target_api_version): 1927 path_expected = None 1928 for specific_api_version in self.specific_api_version_list: 1929 if self.compare_two_versions(target_api_version, specific_api_version) > 0: 1930 continue 1931 path_expected = self.get_path_to_expected( 1932 is_support, "compile", target_api_version, specific_api_version, "asm" 1933 ) 1934 if path.exists(path_expected): 1935 return path_expected 1936 return self.get_path_to_expected(is_support, "compile", "", "", "asm") 1937 1938 def run_process(self, cmd): 1939 self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1940 stdout, stderr = self.process.communicate() 1941 self.output = stdout.decode("utf-8", errors="ignore") + stderr.decode("utf-8", errors="ignore").split("\n")[0] 1942 return stdout, stderr 1943 1944 def run_process_compile(self, runner, target_api_version, target_api_sub_version="bata3", dump_type=""): 1945 cmd = [] 1946 cmd.append(runner.es2panda) 1947 cmd.append(self.path) 1948 cmd.extend(self.flags) 1949 cmd.append("--target-api-version=%s" % (target_api_version)) 1950 test_abc_name = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_") 1951 self.test_abc_path = path.join(runner.build_dir, test_abc_name) 1952 cmd.extend(["--output=%s" % (self.test_abc_path)]) 1953 if target_api_version == "12": 1954 cmd.append("--target-api-sub-version=%s" % (target_api_sub_version)) 1955 if dump_type == "ast": 1956 cmd.append("--dump-ast") 1957 elif dump_type == "assembly": 1958 cmd.append("--dump-assembly") 1959 stdout, stderr = self.run_process(cmd) 1960 return stdout, stderr 1961 1962 def generate_ast_of_target_version(self, runner, target_api_version, target_api_sub_version="bata3"): 1963 return self.run_process_compile(runner, target_api_version, target_api_sub_version, dump_type="ast") 1964 1965 def generate_asm_of_target_version(self, runner, target_api_version, target_api_sub_version="bata3"): 1966 return self.run_process_compile(runner, target_api_version, target_api_sub_version, dump_type="assembly") 1967 1968 def runtime_for_target_version(self, runner, target_api_version, target_api_sub_version="bata3"): 1969 cmd = [] 1970 if target_api_version != "12": 1971 target_api_sub_version = "" 1972 # there is no virtual machine with version api12beta2 available. 1973 # We have chosen api12beta1 as a replacement. 1974 if target_api_version == "12" and target_api_sub_version == "beta2": 1975 target_api_sub_version = "beta1" 1976 ark_js_vm_dir = os.path.join( 1977 runner.build_dir, 1978 "ark_js_vm_version", 1979 "API%s%s" % (target_api_version, target_api_sub_version), 1980 ) 1981 ld_library_path = os.path.join(ark_js_vm_dir, "lib") 1982 os.environ["LD_LIBRARY_PATH"] = ld_library_path 1983 ark_js_vm_path = os.path.join(ark_js_vm_dir, "ark_js_vm") 1984 cmd.append(ark_js_vm_path) 1985 cmd.append(self.test_abc_path) 1986 self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1987 stdout, stderr = self.process.communicate() 1988 self.output = stdout.decode("utf-8", errors="ignore") + stderr.decode("utf-8", errors="ignore").split("\n")[0] 1989 return stdout, stderr 1990 1991 def run_for_single_version(self, runner, target_api_version, target_api_sub_version="beta3"): 1992 cur_api_version = "API" + target_api_version + target_api_sub_version 1993 is_support = True if self.compare_two_versions(cur_api_version, self.test_version) >= 0 else False 1994 compile_expected_path = None 1995 stderr = None 1996 if self.feature_type == "syntax_feature": 1997 compile_expected_path = self.get_path_to_compile_ast_output_expected(is_support) 1998 _, stderr = self.generate_ast_of_target_version(runner, target_api_version, target_api_sub_version) 1999 elif self.feature_type == "bytecode_feature": 2000 compile_expected_path = self.get_path_to_compile_asm_output_expected(is_support, cur_api_version) 2001 _, stderr = self.generate_asm_of_target_version( 2002 runner, target_api_version, target_api_sub_version 2003 ) 2004 try: 2005 with open(compile_expected_path, "r") as fp: 2006 expected = fp.read() 2007 self.passed = expected == self.output and self.process.returncode in [0, 1] 2008 except Exception: 2009 self.passed = False 2010 if not self.passed or (stderr and self.passed): 2011 return stderr 2012 for api_version in self.target_api_version_list: 2013 # The interception capability of API9 version of ark_js_vm has not yet been launched. 2014 if api_version == "9": 2015 continue 2016 for api_sub_version in self.target_api_sub_version_list: 2017 if not api_version in self.version_with_sub_version_list and api_sub_version != "beta3": 2018 continue 2019 cur_runtime_api_version = "API" + api_version + api_sub_version 2020 is_below_abc_version = ( 2021 False if self.compare_two_versions(cur_runtime_api_version, cur_api_version) >= 0 else True 2022 ) 2023 self.generate_module_abc(runner, cur_runtime_api_version) 2024 _, stderr = self.runtime_for_target_version(runner, api_version, api_sub_version) 2025 runtime_expected_path = self.get_path_to_runtime_output_expected( 2026 is_support, cur_api_version, is_below_abc_version 2027 ) 2028 self.remove_module_abc() 2029 try: 2030 with open(runtime_expected_path, "r") as fp: 2031 expected = fp.read() 2032 if is_below_abc_version: 2033 self.passed = expected in self.output 2034 else: 2035 self.passed = expected == self.output 2036 except Exception: 2037 self.passed = False 2038 if not self.passed: 2039 return stderr 2040 return stderr 2041 2042 def run(self, runner): 2043 for target_api_version in self.target_api_version_list: 2044 stderr = None 2045 if target_api_version == "12": 2046 for target_api_sub_version in self.target_api_sub_version_list: 2047 stderr = self.run_for_single_version(runner, target_api_version, target_api_sub_version) 2048 if path.exists(self.test_abc_path): 2049 os.remove(self.test_abc_path) 2050 if not self.passed: 2051 self.error = stderr.decode("utf-8", errors="ignore") 2052 return self 2053 else: 2054 stderr = self.run_for_single_version(runner, target_api_version) 2055 if not self.passed: 2056 self.error = stderr.decode("utf-8", errors="ignore") 2057 return self 2058 return self 2059 2060 2061class CompilerTestInfo(object): 2062 def __init__(self, directory, extension, flags): 2063 self.directory = directory 2064 self.extension = extension 2065 self.flags = flags 2066 2067 def update_dir(self, prefiex_dir): 2068 self.directory = os.path.sep.join([prefiex_dir, self.directory]) 2069 2070 2071# Copy compiler directory to test/.local directory, and do inplace obfuscation. 2072def prepare_for_obfuscation(compiler_test_infos, test_root): 2073 tmp_dir_name = ".local" 2074 tmp_path = os.path.join(test_root, tmp_dir_name) 2075 if not os.path.exists(tmp_path): 2076 os.mkdir(tmp_path) 2077 2078 test_root_dirs = set() 2079 for info in compiler_test_infos: 2080 root_dir = info.directory.split("/")[0] 2081 test_root_dirs.add(root_dir) 2082 2083 for test_dir in test_root_dirs: 2084 src_dir = os.path.join(test_root, test_dir) 2085 target_dir = os.path.join(tmp_path, test_dir) 2086 if os.path.exists(target_dir): 2087 shutil.rmtree(target_dir) 2088 shutil.copytree(src_dir, target_dir) 2089 2090 for info in compiler_test_infos: 2091 info.update_dir(tmp_dir_name) 2092 2093 2094def add_directory_for_version_control(runners, args): 2095 ark_js_vm_prepared = ArkJsVmDownload(args) 2096 ark_js_vm_prepared.run() 2097 runner = VersionControlRunner(args) 2098 runner.add_directory( 2099 "version_control/API11/syntax_feature", 2100 "js", 2101 ["--module"], 2102 "API11", 2103 "syntax_feature", 2104 ) 2105 runner.add_directory( 2106 "version_control/API11/syntax_feature", 2107 "ts", 2108 ["--module"], 2109 "API11", 2110 "syntax_feature", 2111 ) 2112 runner.add_directory( 2113 "version_control/API12beta1_and_beta2/syntax_feature", 2114 "ts", ["--module"], 2115 "API12beta1", 2116 "syntax_feature", 2117 ) 2118 runner.add_directory( 2119 "version_control/API12beta1_and_beta2/syntax_feature", 2120 "js", 2121 ["--module"], 2122 "API12beta1", 2123 "syntax_feature", 2124 ) 2125 runner.add_directory( 2126 "version_control/API12beta3/syntax_feature", 2127 "ts", 2128 ["--module"], 2129 "API12beta3", 2130 "syntax_feature", 2131 "version_control/API12beta3/syntax_feature/import_target", 2132 ) 2133 runner.add_directory( 2134 "version_control/API12beta3/syntax_feature", 2135 "js", 2136 ["--module"], 2137 "API12beta3", 2138 "syntax_feature", 2139 "version_control/API12beta3/syntax_feature/import_target", 2140 ) 2141 runner.add_directory( 2142 "version_control/API11/bytecode_feature", 2143 "ts", 2144 ["--module"], 2145 "API11", 2146 "bytecode_feature", 2147 ) 2148 runner.add_directory( 2149 "version_control/API11/bytecode_feature", 2150 "js", 2151 ["--module"], 2152 "API11", 2153 "bytecode_feature", 2154 ) 2155 runner.add_directory( 2156 "version_control/API12beta1_and_beta2/bytecode_feature", 2157 "ts", 2158 ["--module"], 2159 "API12beta1", 2160 "bytecode_feature", 2161 "version_control/API12beta1_and_beta2/bytecode_feature/import_target", 2162 ) 2163 runner.add_directory( 2164 "version_control/API12beta1_and_beta2/bytecode_feature", 2165 "js", 2166 ["--module"], 2167 "API12beta1", 2168 "bytecode_feature", 2169 "version_control/API12beta1_and_beta2/bytecode_feature/import_target", 2170 ) 2171 runner.add_directory( 2172 "version_control/API12beta3/bytecode_feature", 2173 "ts", 2174 ["--module"], 2175 "API12beta3", 2176 "bytecode_feature", 2177 "version_control/API12beta3/bytecode_feature/import_target", 2178 ) 2179 runner.add_directory( 2180 "version_control/API12beta3/bytecode_feature", 2181 "js", 2182 ["--module"], 2183 "API12beta3", 2184 "bytecode_feature", 2185 "version_control/API12beta3/bytecode_feature/import_target", 2186 ) 2187 runners.append(runner) 2188 2189 abc_tests_prepare = AbcTestCasesPrepare(args) 2190 abc_tests_prepare.gen_abc_tests( 2191 "version_control/bytecode_version_control/non_merge_mode", 2192 "js", 2193 ["--module"], 2194 "non_merge_mode", 2195 ) 2196 abc_tests_prepare.gen_abc_tests( 2197 "version_control/bytecode_version_control/merge_mode", 2198 "js", 2199 ["--module", "--merge-abc"], 2200 "merge_mode", 2201 ) 2202 abc_tests_prepare.gen_abc_tests( 2203 "version_control/bytecode_version_control/mixed_compile", 2204 "js", 2205 ["--module", "--merge-abc"], 2206 "merge_mode", 2207 ) 2208 2209 args.abc_tests_prepare = abc_tests_prepare 2210 abc_version_control_runner = AbcVersionControlRunner(args) 2211 abc_version_control_runner.add_directory( 2212 "version_control/bytecode_version_control/non_merge_mode", 2213 "abc", 2214 ["--module", "--enable-abc-input"], 2215 "non_merge_mode", 2216 ) 2217 abc_version_control_runner.add_directory( 2218 "version_control/bytecode_version_control/merge_mode", 2219 "abc", 2220 ["--module", "--enable-abc-input", "--merge-abc"], 2221 "merge_mode", 2222 ) 2223 abc_version_control_runner.add_directory( 2224 "version_control/bytecode_version_control/mixed_compile", 2225 "txt", 2226 ["--module", "--enable-abc-input", "--merge-abc"], 2227 "mix_compile_mode", 2228 ) 2229 runners.append(abc_version_control_runner) 2230 2231def add_directory_for_regression(runners, args): 2232 runner = RegressionRunner(args) 2233 runner.add_directory("parser/concurrent", "js", ["--module", "--dump-ast"]) 2234 runner.add_directory("parser/js", "js", ["--parse-only", "--dump-ast"]) 2235 runner.add_directory("parser/script", "ts", ["--parse-only", "--dump-ast"]) 2236 runner.add_directory("parser/ts", "ts", 2237 ["--parse-only", "--module", "--dump-ast"]) 2238 runner.add_directory("parser/ts/type_checker", "ts", 2239 ["--parse-only", "--enable-type-check", "--module", "--dump-ast"]) 2240 runner.add_directory("parser/ts/cases/declaration", "d.ts", 2241 ["--parse-only", "--module", "--dump-ast"], TSDeclarationTest) 2242 runner.add_directory("parser/commonjs", "js", ["--commonjs", "--parse-only", "--dump-ast"]) 2243 runner.add_directory("parser/binder", "js", ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"]) 2244 runner.add_directory("parser/binder", "ts", ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"]) 2245 runner.add_directory("parser/binder/noModule", "ts", ["--dump-assembly", "--dump-literal-buffer", "--target-api-sub-version=beta3"]) 2246 runner.add_directory("parser/binder/api12beta2", "js", ["--dump-assembly", "--target-api-version=12", "--target-api-sub-version=beta2"]) 2247 runner.add_directory("parser/js/emptySource", "js", ["--dump-assembly"]) 2248 runner.add_directory("parser/js/language/arguments-object", "js", ["--parse-only"]) 2249 runner.add_directory("parser/js/language/statements/for-statement", "js", ["--parse-only", "--dump-ast"]) 2250 runner.add_directory("parser/js/language/expressions/optional-chain", "js", ["--parse-only", "--dump-ast"]) 2251 runner.add_directory("parser/js/language/import/syntax", "js", 2252 ["--parse-only", "--module", "--target-api-sub-version=beta3"]) 2253 runner.add_directory("parser/js/language/import/syntax/beta2", "js", 2254 ["--parse-only", "--module", "--target-api-version=12", "--target-api-sub-version=beta2"]) 2255 runner.add_directory("parser/js/language/import", "ts", 2256 ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"]) 2257 runner.add_directory("parser/sendable_class", "ts", 2258 ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"]) 2259 runner.add_directory("parser/sendable_class/api12beta2", "ts", 2260 ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-version=12", "--target-api-sub-version=beta2"]) 2261 runner.add_directory("parser/unicode", "js", ["--parse-only"]) 2262 runner.add_directory("parser/ts/stack_overflow", "ts", ["--parse-only", "--dump-ast"]) 2263 runner.add_directory("parser/js/module-record/module-record-field-name-option.js", "js", 2264 ["--module-record-field-name=abc", "--source-file=abc", "--module", "--dump-normalized-asm-program"]) 2265 runner.add_directory("parser/annotations", "ts", ["--module", "--dump-ast", "--enable-annotations"]) 2266 2267 runners.append(runner) 2268 2269 transformer_runner = TransformerRunner(args) 2270 transformer_runner.add_directory("parser/ts/transformed_cases", "ts", 2271 ["--parse-only", "--module", "--dump-transformed-ast", 2272 "--check-transformed-ast-structure"]) 2273 2274 runners.append(transformer_runner) 2275 2276 bc_version_runner = BcVersionRunner(args) 2277 bc_version_runner.add_cmd() 2278 2279 runners.append(bc_version_runner) 2280 2281 transformer_api_version_10_runner = TransformerInTargetApiVersion10Runner(args) 2282 transformer_api_version_10_runner.add_directory("parser/ts/transformed_cases_api_version_10", "ts", 2283 ["--parse-only", "--module", "--target-api-version=10", 2284 "--dump-transformed-ast"]) 2285 2286 runners.append(transformer_api_version_10_runner) 2287 2288def add_directory_for_asm(runners, args, mode=""): 2289 runner = AbcToAsmRunner(args, True if mode == "debug" else False) 2290 runner.add_directory("abc2asm/js", "js", []) 2291 runner.add_directory("abc2asm/ts", "ts", []) 2292 runner.add_directory("compiler/js", "js", []) 2293 runner.add_directory("compiler/ts/cases/compiler", "ts", []) 2294 runner.add_directory("compiler/ts/projects", "ts", ["--module"]) 2295 runner.add_directory("compiler/ts/projects", "ts", ["--module", "--merge-abc"]) 2296 runner.add_directory("compiler/dts", "d.ts", ["--module", "--opt-level=0"]) 2297 runner.add_directory("compiler/commonjs", "js", ["--commonjs"]) 2298 runner.add_directory("parser/concurrent", "js", ["--module"]) 2299 runner.add_directory("parser/js", "js", []) 2300 runner.add_directory("parser/script", "ts", []) 2301 runner.add_directory("parser/ts", "ts", ["--module"]) 2302 runner.add_directory("parser/ts/type_checker", "ts", ["--enable-type-check", "--module"]) 2303 runner.add_directory("parser/commonjs", "js", ["--commonjs"]) 2304 runner.add_directory("parser/binder", "js", ["--dump-assembly", "--dump-literal-buffer", "--module"]) 2305 runner.add_directory("parser/binder", "ts", ["--dump-assembly", "--dump-literal-buffer", "--module"]) 2306 runner.add_directory("parser/binder/noModule", "ts", ["--dump-assembly", "--dump-literal-buffer"]) 2307 runner.add_directory("parser/js/emptySource", "js", []) 2308 runner.add_directory("parser/js/language/arguments-object", "js", []) 2309 runner.add_directory("parser/js/language/statements/for-statement", "js", []) 2310 runner.add_directory("parser/js/language/expressions/optional-chain", "js", []) 2311 runner.add_directory("parser/sendable_class", "ts", ["--module"]) 2312 runner.add_directory("parser/unicode", "js", []) 2313 runner.add_directory("parser/ts/stack_overflow", "ts", []) 2314 2315 runners.append(runner) 2316 2317 2318def add_directory_for_compiler(runners, args): 2319 runner = CompilerRunner(args) 2320 compiler_test_infos = [] 2321 compiler_test_infos.append(CompilerTestInfo("compiler/js", "js", ["--module"])) 2322 compiler_test_infos.append(CompilerTestInfo("compiler/ts/cases", "ts", [])) 2323 compiler_test_infos.append(CompilerTestInfo("compiler/ts/projects", "ts", ["--module"])) 2324 compiler_test_infos.append(CompilerTestInfo("compiler/ts/projects", "ts", ["--module", "--merge-abc"])) 2325 compiler_test_infos.append(CompilerTestInfo("compiler/annotations-projects", "ts", ["--module", "--enable-annotations", "--merge-abc"])) 2326 compiler_test_infos.append(CompilerTestInfo("compiler/dts", "d.ts", ["--module", "--opt-level=0"])) 2327 compiler_test_infos.append(CompilerTestInfo("compiler/commonjs", "js", ["--commonjs"])) 2328 compiler_test_infos.append(CompilerTestInfo("compiler/interpreter/lexicalEnv", "js", [])) 2329 compiler_test_infos.append(CompilerTestInfo("compiler/sendable", "ts", ["--module", "--target-api-sub-version=beta3"])) 2330 compiler_test_infos.append(CompilerTestInfo("optimizer/js/branch-elimination", "js", 2331 ["--module", "--branch-elimination", "--dump-assembly"])) 2332 compiler_test_infos.append(CompilerTestInfo("optimizer/js/opt-try-catch-func", "js", 2333 ["--module", "--dump-assembly"])) 2334 compiler_test_infos.append(CompilerTestInfo("compiler/debugInfo/", "js", 2335 ["--debug-info", "--dump-debug-info", "--source-file", "debug-info.js"])) 2336 compiler_test_infos.append(CompilerTestInfo("compiler/js/module-record-field-name-option.js", "js", 2337 ["--module", "--module-record-field-name=abc"])) 2338 compiler_test_infos.append(CompilerTestInfo("compiler/annotations", "ts", ["--module", "--enable-annotations"])) 2339 # Following directories of test cases are for dump-assembly comparison only, and is not executed. 2340 # Check CompilerProjectTest for more details. 2341 compiler_test_infos.append(CompilerTestInfo("optimizer/ts/branch-elimination/projects", "ts", 2342 ["--module", "--branch-elimination", "--merge-abc", "--dump-assembly", 2343 "--file-threads=8"])) 2344 compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/projects", "ts", 2345 ["--merge-abc", "--dump-assembly", "--enable-abc-input", 2346 "--dump-deps-info", "--remove-redundant-file", 2347 "--dump-literal-buffer", "--dump-string", "--abc-class-threads=4"])) 2348 compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/js/projects", "js", 2349 ["--merge-abc", "--dump-assembly", "--enable-abc-input", 2350 "--dump-deps-info", "--remove-redundant-file", 2351 "--dump-literal-buffer", "--dump-string", "--abc-class-threads=4"])) 2352 compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/merge_abc_consistence_check/projects", "js", 2353 ["--merge-abc", "--dump-assembly", "--enable-abc-input", 2354 "--abc-class-threads=4"])) 2355 2356 compiler_test_infos.append(CompilerTestInfo("compiler/ts/shared_module/projects", "ts", 2357 ["--module", "--merge-abc", "--dump-assembly"])) 2358 2359 if args.enable_arkguard: 2360 prepare_for_obfuscation(compiler_test_infos, runner.test_root) 2361 2362 for info in compiler_test_infos: 2363 runner.add_directory(info.directory, info.extension, info.flags) 2364 2365 runners.append(runner) 2366 2367 2368def add_directory_for_bytecode(runners, args): 2369 runner = BytecodeRunner(args) 2370 runner.add_directory("bytecode/commonjs", "js", ["--commonjs", "--dump-assembly"]) 2371 runner.add_directory("bytecode/js", "js", ["--dump-assembly"]) 2372 runner.add_directory("bytecode/ts/cases", "ts", ["--dump-assembly"]) 2373 runner.add_directory("bytecode/ts/ic", "ts", ["--dump-assembly"]) 2374 runner.add_directory("bytecode/ts/api11", "ts", ["--dump-assembly", "--module", "--target-api-version=11"]) 2375 runner.add_directory("bytecode/ts/api12", "ts", ["--dump-assembly", "--module", "--target-api-version=12"]) 2376 runner.add_directory("bytecode/watch-expression", "js", ["--debugger-evaluate-expression", "--dump-assembly"]) 2377 2378 runners.append(runner) 2379 2380 2381def add_directory_for_debug(runners, args): 2382 runner = RegressionRunner(args) 2383 runner.add_directory("debug/parser", "js", ["--parse-only", "--dump-ast"]) 2384 2385 runners.append(runner) 2386 2387 2388def add_cmd_for_aop_transform(runners, args): 2389 runner = AopTransform(args) 2390 2391 aop_file_path = path.join(runner.test_root, "aop") 2392 lib_suffix = '.so' 2393 #cpp src, deal type, result compare str, abc compare str 2394 msg_list = [ 2395 ["correct_modify.cpp", "compile", "aop_transform_start", "new_abc_content"], 2396 ["correct_no_modify.cpp", "compile", "aop_transform_start", ""], 2397 ["exec_error.cpp", "compile", "Transform exec fail", ""], 2398 ["no_func_transform.cpp", "compile", "os::library_loader::ResolveSymbol get func Transform error", ""], 2399 ["error_format.cpp", "copy_lib", "os::library_loader::Load error", ""], 2400 ["".join(["no_exist", lib_suffix]), "dirct_use", "Failed to find file", ""], 2401 ["error_suffix.xxx", "direct_use", "aop transform file suffix support", ""] 2402 ] 2403 for msg in msg_list: 2404 cpp_file = path.join(aop_file_path, msg[0]) 2405 if msg[1] == 'compile': 2406 lib_file = cpp_file.replace('.cpp', lib_suffix) 2407 remove_file = lib_file 2408 runner.add_cmd(["g++", "--share", "-o", lib_file, cpp_file], "", "", "") 2409 elif msg[1] == 'copy_lib': 2410 lib_file = cpp_file.replace('.cpp', lib_suffix) 2411 remove_file = lib_file 2412 if not os.path.exists(lib_file): 2413 with open(cpp_file, "r") as source_file: 2414 fd = os.open(lib_file, os.O_RDWR | os.O_CREAT | os.O_TRUNC) 2415 target_file = os.fdopen(fd, 'w') 2416 target_file.write(source_file.read()) 2417 elif msg[1] == 'direct_use': 2418 lib_file = cpp_file 2419 remove_file = "" 2420 2421 js_file = path.join(aop_file_path, "test_aop.js") 2422 runner.add_cmd([runner.es2panda, "--merge-abc", "--transform-lib", lib_file, js_file], msg[2], msg[3], remove_file) 2423 2424 runners.append(runner) 2425 2426 2427class AopTransform(Runner): 2428 def __init__(self, args): 2429 Runner.__init__(self, args, "AopTransform") 2430 2431 def add_cmd(self, cmd, compare_str, compare_abc_str, remove_file, func=TestAop): 2432 self.tests += [func(cmd, compare_str, compare_abc_str, remove_file)] 2433 2434 def test_path(self, src): 2435 return src 2436 2437 2438def main(): 2439 args = get_args() 2440 2441 runners = [] 2442 2443 if args.regression: 2444 add_directory_for_regression(runners, args) 2445 2446 if args.abc_to_asm: 2447 add_directory_for_asm(runners, args) 2448 add_directory_for_asm(runners, args, "debug") 2449 2450 if args.tsc: 2451 runners.append(TSCRunner(args)) 2452 2453 if args.compiler: 2454 add_directory_for_compiler(runners, args) 2455 2456 if args.hotfix: 2457 runners.append(HotfixRunner(args)) 2458 2459 if args.hotreload: 2460 runners.append(HotreloadRunner(args)) 2461 2462 if args.coldfix: 2463 runners.append(ColdfixRunner(args)) 2464 2465 if args.coldreload: 2466 runners.append(ColdreloadRunner(args)) 2467 2468 if args.debugger: 2469 runners.append(DebuggerRunner(args)) 2470 2471 if args.base64: 2472 runners.append(Base64Runner(args)) 2473 2474 if args.bytecode: 2475 add_directory_for_bytecode(runners, args) 2476 2477 if args.aop_transform: 2478 add_cmd_for_aop_transform(runners, args) 2479 2480 if args.debug: 2481 add_directory_for_debug(runners, args) 2482 2483 if args.version_control: 2484 add_directory_for_version_control(runners, args) 2485 2486 failed_tests = 0 2487 2488 for runner in runners: 2489 runner.run() 2490 failed_tests += runner.summarize() 2491 2492 # TODO: exit 1 when we have failed tests after all tests are fixed 2493 exit(0) 2494 2495 2496if __name__ == "__main__": 2497 main() 2498