• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
28
29
30def is_directory(parser, arg):
31    if not path.isdir(arg):
32        parser.error("The directory '%s' does not exist" % arg)
33
34    return path.abspath(arg)
35
36
37def is_file(parser, arg):
38    if not path.isfile(arg):
39        parser.error("The file '%s' does not exist" % arg)
40
41    return path.abspath(arg)
42
43def prepare_tsc_testcases(test_root):
44    third_party_tsc = path.join(test_root, "TypeScript")
45    ohos_third_party_tsc = path.join(test_root, "../../../../third_party/typescript")
46
47    if not path.isdir(third_party_tsc):
48        if (path.isdir(ohos_third_party_tsc)):
49            return path.abspath(ohos_third_party_tsc)
50        subprocess.run(
51            f"git clone https://gitee.com/openharmony/third_party_typescript.git {third_party_tsc}",
52            shell=True,
53            stdout=subprocess.DEVNULL,
54        )
55    else:
56        subprocess.run(
57            f"cd {third_party_tsc} && git clean -f > /dev/null 2>&1",
58            shell=True,
59            stdout=subprocess.DEVNULL,
60        )
61    return third_party_tsc
62
63def check_timeout(value):
64    ivalue = int(value)
65    if ivalue <= 0:
66        raise argparse.ArgumentTypeError(
67            "%s is an invalid timeout value" % value)
68    return ivalue
69
70
71def get_args():
72    parser = argparse.ArgumentParser(description="Regression test runner")
73    parser.add_argument(
74        'build_dir', type=lambda arg: is_directory(parser, arg),
75        help='panda build directory')
76    parser.add_argument(
77        '--error', action='store_true', dest='error', default=False,
78        help='capture stderr')
79    parser.add_argument(
80        '--abc-to-asm', action='store_true', dest='abc_to_asm',
81        default=False, help='run abc2asm tests')
82    parser.add_argument(
83        '--regression', '-r', action='store_true', dest='regression',
84        default=False, help='run regression tests')
85    parser.add_argument(
86        '--compiler', '-c', action='store_true', dest='compiler',
87        default=False, help='run compiler tests')
88    parser.add_argument(
89        '--tsc', action='store_true', dest='tsc',
90        default=False, help='run tsc tests')
91    parser.add_argument(
92        '--no-progress', action='store_false', dest='progress', default=True,
93        help='don\'t show progress bar')
94    parser.add_argument(
95        '--no-skip', action='store_false', dest='skip', default=True,
96        help='don\'t use skiplists')
97    parser.add_argument(
98        '--update', action='store_true', dest='update', default=False,
99        help='update skiplist')
100    parser.add_argument(
101        '--no-run-gc-in-place', action='store_true', dest='no_gip', default=False,
102        help='enable --run-gc-in-place mode')
103    parser.add_argument(
104        '--filter', '-f', action='store', dest='filter',
105        default="*", help='test filter regexp')
106    parser.add_argument(
107        '--es2panda-timeout', type=check_timeout,
108        dest='es2panda_timeout', default=60, help='es2panda translator timeout')
109    parser.add_argument(
110        '--paoc-timeout', type=check_timeout,
111        dest='paoc_timeout', default=600, help='paoc compiler timeout')
112    parser.add_argument(
113        '--timeout', type=check_timeout,
114        dest='timeout', default=10, help='JS runtime timeout')
115    parser.add_argument(
116        '--gc-type', dest='gc_type', default="g1-gc", help='Type of garbage collector')
117    parser.add_argument(
118        '--aot', action='store_true', dest='aot', default=False,
119        help='use AOT compilation')
120    parser.add_argument(
121        '--no-bco', action='store_false', dest='bco', default=True,
122        help='disable bytecodeopt')
123    parser.add_argument(
124        '--jit', action='store_true', dest='jit', default=False,
125        help='use JIT in interpreter')
126    parser.add_argument(
127        '--arm64-compiler-skip', action='store_true', dest='arm64_compiler_skip', default=False,
128        help='use skiplist for tests failing on aarch64 in AOT or JIT mode')
129    parser.add_argument(
130        '--arm64-qemu', action='store_true', dest='arm64_qemu', default=False,
131        help='launch all binaries in qemu aarch64')
132    parser.add_argument(
133        '--arm32-qemu', action='store_true', dest='arm32_qemu', default=False,
134        help='launch all binaries in qemu arm')
135    parser.add_argument(
136        '--test-list', dest='test_list', default=None, type=lambda arg: is_file(parser, arg),
137        help='run tests listed in file')
138    parser.add_argument(
139        '--aot-args', action='append', dest='aot_args', default=[],
140        help='Additional arguments that will passed to ark_aot')
141    parser.add_argument(
142        '--verbose', '-v', action='store_true', dest='verbose', default=False,
143        help='Enable verbose output')
144    parser.add_argument(
145        '--js-runtime', dest='js_runtime_path', default=None, type=lambda arg: is_directory(parser, arg),
146        help='the path of js vm runtime')
147    parser.add_argument(
148        '--LD_LIBRARY_PATH', dest='ld_library_path', default=None, help='LD_LIBRARY_PATH')
149    parser.add_argument(
150        '--tsc-path', dest='tsc_path', default=None, type=lambda arg: is_directory(parser, arg),
151        help='the path of tsc')
152    parser.add_argument('--hotfix', dest='hotfix', action='store_true', default=False,
153        help='run hotfix tests')
154    parser.add_argument('--hotreload', dest='hotreload', action='store_true', default=False,
155        help='run hotreload tests')
156    parser.add_argument('--coldfix', dest='coldfix', action='store_true', default=False,
157        help='run coldfix tests')
158    parser.add_argument('--coldreload', dest='coldreload', action='store_true', default=False,
159        help='run coldreload tests')
160    parser.add_argument('--base64', dest='base64', action='store_true', default=False,
161        help='run base64 tests')
162    parser.add_argument('--bytecode', dest='bytecode', action='store_true', default=False,
163        help='run bytecode tests')
164    parser.add_argument('--debugger', dest='debugger', action='store_true', default=False,
165        help='run debugger tests')
166    parser.add_argument('--debug', dest='debug', action='store_true', default=False,
167        help='run debug tests')
168    parser.add_argument('--enable-arkguard', action='store_true', dest='enable_arkguard', default=False,
169        help='enable arkguard for compiler tests')
170    parser.add_argument('--aop-transform', dest='aop_transform', action='store_true', default=False,
171        help='run debug tests')
172
173    return parser.parse_args()
174
175
176def run_subprocess_with_beta3(test_obj, cmd):
177    has_target_api = False
178    has_version_12 = False
179    has_sub_version = False
180    is_es2abc_cmd = False
181
182    for param in cmd:
183        if "es2abc" in param:
184            is_es2abc_cmd = True
185        if "--target-api-sub-version" in param:
186            has_sub_version = True
187        if "--target-api-version" in param:
188            has_target_api = True
189        if "12" in param:
190            has_version_12 = True
191    if is_es2abc_cmd and (not has_target_api or (has_version_12 and not has_sub_version)):
192        cmd.append("--target-api-sub-version=beta3")
193    if test_obj:
194        test_obj.log_cmd(cmd)
195    return subprocess.Popen(
196        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
197
198
199class Test:
200    def __init__(self, test_path, flags):
201        self.path = test_path
202        self.flags = flags
203        self.output = None
204        self.error = None
205        self.passed = None
206        self.skipped = None
207        self.reproduce = ""
208
209    def log_cmd(self, cmd):
210        self.reproduce += "\n" + ' '.join(cmd)
211
212    def get_path_to_expected(self):
213        if self.path.find(".d.ts") == -1:
214            return "%s-expected.txt" % (path.splitext(self.path)[0])
215        return "%s-expected.txt" % (self.path[:self.path.find(".d.ts")])
216
217    def run(self, runner):
218        test_abc_name = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_")
219        test_abc_path = path.join(runner.build_dir, test_abc_name)
220        cmd = runner.cmd_prefix + [runner.es2panda]
221        cmd.extend(self.flags)
222        cmd.extend(["--output=" + test_abc_path])
223        cmd.append(self.path)
224        process = run_subprocess_with_beta3(self, cmd)
225        out, err = process.communicate()
226        self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
227
228        expected_path = self.get_path_to_expected()
229        try:
230            with open(expected_path, 'r') as fp:
231                expected = fp.read()
232            self.passed = expected == self.output and process.returncode in [
233                0, 1]
234        except Exception:
235            self.passed = False
236
237        if not self.passed:
238            self.error = err.decode("utf-8", errors="ignore")
239
240        if os.path.exists(test_abc_path):
241            os.remove(test_abc_path)
242
243        return self
244
245
246class TSCTest(Test):
247    def __init__(self, test_path, flags):
248        Test.__init__(self, test_path, flags)
249        self.options = self.parse_options()
250
251    def parse_options(self):
252        test_options = {}
253
254        with open(self.path, "r", encoding="latin1") as f:
255            lines = f.read()
256            options = re.findall(r"//\s?@\w+:.*\n", lines)
257
258            for option in options:
259                separated = option.split(":")
260                opt = re.findall(r"\w+", separated[0])[0].lower()
261                value = separated[1].strip().lower()
262
263                if opt == "filename":
264                    if opt in options:
265                        test_options[opt].append(value)
266                    else:
267                        test_options[opt] = [value]
268
269                elif opt == "lib" or opt == "module":
270                    test_options[opt] = [each.strip()
271                                         for each in value.split(",")]
272                elif value == "true" or value == "false":
273                    test_options[opt] = value.lower() == "true"
274                else:
275                    test_options[opt] = value
276
277            # TODO: Possibility of error: all exports will be catched, even the commented ones
278            if 'module' not in test_options and re.search(r"export ", lines):
279                test_options['module'] = []
280
281        return test_options
282
283    def run(self, runner):
284        cmd = runner.cmd_prefix + [runner.es2panda, '--parse-only']
285        cmd.extend(self.flags)
286        if "module" in self.options:
287            cmd.append('--module')
288        cmd.append(self.path)
289        process = run_subprocess_with_beta3(self, cmd)
290        out, err = process.communicate()
291        self.output = out.decode("utf-8", errors="ignore")
292
293        self.passed = True if process.returncode == 0 else False
294
295        if not self.passed:
296            self.error = err.decode("utf-8", errors="ignore")
297
298        return self
299
300
301class TestAop:
302    def __init__(self, cmd, compare_str, compare_abc_str, remove_file):
303        self.cmd = cmd
304        self.compare_str = compare_str
305        self.compare_abc_str = compare_abc_str
306        self.remove_file = remove_file
307        self.path = ''
308        self.output = None
309        self.error = None
310        self.passed = None
311        self.skipped = None
312        self.reproduce = ""
313
314    def log_cmd(self, cmd):
315        self.reproduce += ''.join(["\n", ' '.join(cmd)])
316
317    def run(self, runner):
318        cmd = self.cmd
319        process = run_subprocess_with_beta3(self, cmd)
320        out, err = process.communicate()
321        self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
322
323        if self.compare_str == '':
324            self.passed = True
325        else :
326            self.passed = self.output.startswith(self.compare_str) and process.returncode in [0, 1]
327            if self.remove_file != '' and os.path.exists(self.remove_file):
328                os.remove(self.remove_file)
329
330        if not self.passed:
331            self.error = err.decode("utf-8", errors="ignore")
332
333        abc_path = path.join(os.getcwd(), 'test_aop.abc')
334        if os.path.exists(abc_path):
335            if self.compare_abc_str != '':
336                with open(abc_path, "r") as abc_file:
337                    self.passed = self.passed and abc_file.read() == self.compare_abc_str
338            os.remove(abc_path)
339
340        return self
341
342
343class Runner:
344    def __init__(self, args, name):
345        self.test_root = path.dirname(path.abspath(__file__))
346        self.args = args
347        self.name = name
348        self.tests = []
349        self.failed = 0
350        self.passed = 0
351        self.es2panda = path.join(args.build_dir, 'es2abc')
352        self.build_dir = args.build_dir
353        self.cmd_prefix = []
354        self.ark_js_vm = ""
355        self.ark_aot_compiler = ""
356        self.ld_library_path = ""
357
358        if args.js_runtime_path:
359            self.ark_js_vm = path.join(args.js_runtime_path, 'ark_js_vm')
360            self.ark_aot_compiler = path.join(args.js_runtime_path, 'ark_aot_compiler')
361
362        if args.ld_library_path:
363            self.ld_library_path = args.ld_library_path
364
365        if args.arm64_qemu:
366            self.cmd_prefix = ["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu/"]
367
368        if args.arm32_qemu:
369            self.cmd_prefix = ["qemu-arm", "-L", "/usr/arm-linux-gnueabi"]
370
371        if not path.isfile(self.es2panda):
372            raise Exception("Cannot find es2panda binary: %s" % self.es2panda)
373
374    def add_directory(self, directory, extension, flags):
375        pass
376
377    def test_path(self, src):
378        pass
379
380    def run_test(self, test):
381        return test.run(self)
382
383    def run(self):
384        pool = multiprocessing.Pool()
385        result_iter = pool.imap_unordered(
386            self.run_test, self.tests, chunksize=32)
387        pool.close()
388
389        if self.args.progress:
390            from tqdm import tqdm
391            result_iter = tqdm(result_iter, total=len(self.tests))
392
393        results = []
394        for res in result_iter:
395            results.append(res)
396
397        self.tests = results
398        pool.join()
399
400    def deal_error(self, test):
401        path_str = test.path
402        err_col = {}
403        if test.error:
404            err_str = test.error.split('[')[0] if "patchfix" not in test.path else " patchfix throw error failed"
405            err_col = {"path" : [path_str], "status": ["fail"], "error" : [test.error], "type" : [err_str]}
406        else:
407            err_col = {"path" : [path_str], "status": ["fail"], "error" : ["Segmentation fault"],
408                        "type" : ["Segmentation fault"]}
409        return err_col
410
411    def summarize(self):
412        print("")
413        fail_list = []
414        success_list = []
415
416        for test in self.tests:
417            assert(test.passed is not None)
418            if not test.passed:
419                fail_list.append(test)
420            else:
421                success_list.append(test)
422
423        if len(fail_list):
424            if self.args.error:
425                import pandas as pd
426                test_list = pd.DataFrame(columns=["path", "status", "error", "type"])
427            for test in success_list:
428                suc_col = {"path" : [test.path], "status": ["success"], "error" : ["success"], "type" : ["success"]}
429                if self.args.error:
430                    test_list = pd.concat([test_list, pd.DataFrame(suc_col)])
431            print("Failed tests:")
432            for test in fail_list:
433                print(self.test_path(test.path))
434
435                if self.args.error:
436                    print("steps:", test.reproduce)
437                    print("error:")
438                    print(test.error)
439                    print("\n")
440                    err_col = self.deal_error(test)
441                    test_list = pd.concat([test_list, pd.DataFrame(err_col)])
442
443            if self.args.error:
444                test_list.to_csv('test_statistics.csv', index=False)
445                test_list["type"].value_counts().to_csv('type_statistics.csv', index_label="error")
446                print("Type statistics:\n", test_list["type"].value_counts())
447            print("")
448
449        print("Summary(%s):" % self.name)
450        print("\033[37mTotal:   %5d" % (len(self.tests)))
451        print("\033[92mPassed:  %5d" % (len(self.tests) - len(fail_list)))
452        print("\033[91mFailed:  %5d" % (len(fail_list)))
453        print("\033[0m")
454
455        return len(fail_list)
456
457
458class RegressionRunner(Runner):
459    def __init__(self, args):
460        Runner.__init__(self, args, "Regression")
461
462    def add_directory(self, directory, extension, flags, func=Test):
463        glob_expression = path.join(
464            self.test_root, directory, "*.%s" % (extension))
465        files = glob(glob_expression)
466        files = fnmatch.filter(files, self.test_root + '**' + self.args.filter)
467
468        self.tests += list(map(lambda f: func(f, flags), files))
469
470    def test_path(self, src):
471        return src
472
473
474class AbcToAsmRunner(Runner):
475    def __init__(self, args, is_debug):
476        Runner.__init__(self, args, "Abc2asm" if not is_debug else "Abc2asmDebug")
477        self.is_debug = is_debug
478
479    def add_directory(self, directory, extension, flags, func=Test):
480        glob_expression = path.join(
481            self.test_root, directory, "*.%s" % (extension))
482        files = glob(glob_expression)
483        files = fnmatch.filter(files, self.test_root + '**' + self.args.filter)
484
485        self.tests += list(map(lambda f: AbcToAsmTest(f, flags, self.is_debug), files))
486
487    def test_path(self, src):
488        return os.path.basename(src)
489
490
491class AbcToAsmTest(Test):
492    def __init__(self, test_path, flags, is_debug):
493        Test.__init__(self, test_path, flags)
494        self.is_debug = is_debug
495
496    def run(self, runner):
497        output_abc_file = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_")
498        # source code compilation, generate an abc file
499        gen_abc_cmd = runner.cmd_prefix + [runner.es2panda]
500        if (self.is_debug):
501            gen_abc_cmd.extend(["--debug-info"])
502        gen_abc_cmd.extend(["--module", "--dump-normalized-asm-program", "--output=" + output_abc_file])
503        gen_abc_cmd.append(self.path)
504        process_gen_abc = run_subprocess_with_beta3(self, gen_abc_cmd)
505        gen_abc_out, gen_abc_err = process_gen_abc.communicate()
506        gen_abc_output = gen_abc_out.decode("utf-8", errors="ignore")
507
508        # If no abc file is generated, an error occurs during parser, but abc2asm function is normal.
509        if not os.path.exists(output_abc_file):
510            self.passed = True
511            return self
512
513        # abc file compilation
514        abc_to_asm_cmd = runner.cmd_prefix + [runner.es2panda]
515        if (self.is_debug):
516            abc_to_asm_cmd.extend(["--debug-info"])
517        abc_to_asm_cmd.extend(["--module", "--dump-normalized-asm-program", "--enable-abc-input"])
518        abc_to_asm_cmd.append(output_abc_file)
519        process_abc_to_asm = run_subprocess_with_beta3(self, abc_to_asm_cmd)
520        abc_to_asm_out, abc_to_asm_err = process_abc_to_asm.communicate()
521        abc_to_asm_output = abc_to_asm_out.decode("utf-8", errors="ignore")
522
523        self.passed = gen_abc_output == abc_to_asm_output and process_abc_to_asm.returncode in [0, 1]
524        if not self.passed:
525            self.error = "Comparison of dump results between source code compilation and abc file compilation failed."
526            if gen_abc_err:
527                self.error += "\n" + gen_abc_err.decode("utf-8", errors="ignore")
528            if abc_to_asm_err:
529                self.error += "\n" + abc_to_asm_err.decode("utf-8", errors="ignore")
530
531        os.remove(output_abc_file)
532        return self
533
534
535class TSCRunner(Runner):
536    def __init__(self, args):
537        Runner.__init__(self, args, "TSC")
538
539        if self.args.tsc_path:
540            self.tsc_path = self.args.tsc_path
541        else :
542            self.tsc_path = prepare_tsc_testcases(self.test_root)
543
544        self.add_directory("conformance", [])
545        self.add_directory("compiler", [])
546
547    def add_directory(self, directory, flags):
548        ts_suite_dir = path.join(self.tsc_path, 'tests/cases')
549
550        glob_expression = path.join(
551            ts_suite_dir, directory, "**/*.ts")
552        files = glob(glob_expression, recursive=True)
553        files = fnmatch.filter(files, ts_suite_dir + '**' + self.args.filter)
554
555        for f in files:
556            test_name = path.basename(f.split(".ts")[0])
557            negative_references = path.join(
558                self.tsc_path, 'tests/baselines/reference')
559            is_negative = path.isfile(path.join(negative_references,
560                                                test_name + ".errors.txt"))
561            test = TSCTest(f, flags)
562
563            if 'target' in test.options:
564                targets = test.options['target'].replace(" ", "").split(',')
565                for target in targets:
566                    if path.isfile(path.join(negative_references,
567                                             test_name + "(target=%s).errors.txt" % (target))):
568                        is_negative = True
569                        break
570
571            if is_negative or "filename" in test.options:
572                continue
573
574            with open(path.join(self.test_root, 'test_tsc_ignore_list.txt'), 'r') as failed_references:
575                if self.args.skip:
576                    if path.relpath(f, self.tsc_path) in failed_references.read():
577                        continue
578
579            self.tests.append(test)
580
581    def test_path(self, src):
582        return src
583
584
585class CompilerRunner(Runner):
586    def __init__(self, args):
587        Runner.__init__(self, args, "Compiler")
588
589    def add_directory(self, directory, extension, flags):
590        if directory.endswith("projects"):
591            projects_path = path.join(self.test_root, directory)
592            for project in os.listdir(projects_path):
593                glob_expression = path.join(projects_path, project, "**/*.%s" % (extension))
594                files = glob(glob_expression, recursive=True)
595                files = fnmatch.filter(files, self.test_root + '**' + self.args.filter)
596                self.tests.append(CompilerProjectTest(projects_path, project, files, flags))
597        else:
598            glob_expression = path.join(
599                self.test_root, directory, "**/*.%s" % (extension))
600            files = glob(glob_expression, recursive=True)
601            files = fnmatch.filter(files, self.test_root + '**' + self.args.filter)
602            self.tests += list(map(lambda f: CompilerTest(f, flags), files))
603
604    def test_path(self, src):
605        return src
606
607
608class CompilerTest(Test):
609    def __init__(self, test_path, flags):
610        Test.__init__(self, test_path, flags)
611
612    def execute_arkguard(self, runner):
613        input_file_path = self.path
614        arkguard_root_dir = os.path.join(runner.test_root, "../../arkguard")
615        arkgurad_entry_path = os.path.join(arkguard_root_dir, "lib/cli/SecHarmony.js")
616        config_path = os.path.join(arkguard_root_dir, "test/compilerTestConfig.json")
617        arkguard_cmd = [
618            'node',
619            '--no-warnings',
620            arkgurad_entry_path,
621            input_file_path,
622            '--config-path',
623            config_path,
624            '--inplace'
625        ]
626        self.log_cmd(arkguard_cmd)
627        process = subprocess.Popen(arkguard_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
628        out, err = process.communicate()
629        process.wait()
630        success = True
631        if err or process.returncode != 0:
632            success = False
633            self.passed = False
634            self.error = err.decode("utf-8", errors="ignore")
635        return success
636
637    def run(self, runner):
638        test_abc_name = ("%s.abc" % (path.splitext(self.path)[0])).replace("/", "_")
639        test_abc_path = path.join(runner.build_dir, test_abc_name)
640        es2abc_cmd = runner.cmd_prefix + [runner.es2panda]
641        es2abc_cmd.extend(self.flags)
642        es2abc_cmd.extend(["--output=" + test_abc_path])
643        es2abc_cmd.append(self.path)
644        enable_arkguard = runner.args.enable_arkguard
645        if enable_arkguard:
646            success = self.execute_arkguard(runner)
647            if not success:
648                return self
649
650        process = run_subprocess_with_beta3(self, es2abc_cmd)
651        out, err = process.communicate()
652        if "--dump-assembly" in self.flags:
653            pa_expected_path = "".join([self.get_path_to_expected()[:self.get_path_to_expected().rfind(".txt")],
654                                       ".pa.txt"])
655            self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
656            try:
657                with open(pa_expected_path, 'r') as fp:
658                    expected = fp.read()
659                self.passed = expected == self.output and process.returncode in [0, 1]
660            except Exception:
661                self.passed = False
662            if not self.passed:
663                self.error = err.decode("utf-8", errors="ignore")
664                if os.path.exists(test_abc_path):
665                    os.remove(test_abc_path)
666                return self
667        if "--dump-debug-info" in self.flags:
668            self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
669            try:
670                with open(self.get_path_to_expected(), 'r') as fp:
671                    expected = fp.read()
672                self.passed = expected == self.output and process.returncode in [0, 1]
673                if os.path.exists(test_abc_path):
674                    os.remove(test_abc_path)
675                return self
676            except Exception:
677                self.passed = False
678            if not self.passed:
679                self.error = err.decode("utf-8", errors="ignore")
680                if os.path.exists(test_abc_path):
681                    os.remove(test_abc_path)
682                return self
683        if err:
684            self.passed = False
685            self.error = err.decode("utf-8", errors="ignore")
686            return self
687
688        ld_library_path = runner.ld_library_path
689        os.environ.setdefault("LD_LIBRARY_PATH", ld_library_path)
690        run_abc_cmd = [runner.ark_js_vm, '--enable-force-gc=false', test_abc_path]
691        self.log_cmd(run_abc_cmd)
692
693        process = subprocess.Popen(run_abc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
694        out, err = process.communicate()
695        self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
696        expected_path = self.get_path_to_expected()
697        try:
698            with open(expected_path, 'r') as fp:
699                expected = fp.read()
700            self.passed = expected == self.output and process.returncode in [0, 1]
701        except Exception:
702            self.passed = False
703
704        if not self.passed:
705            self.error = err.decode("utf-8", errors="ignore")
706
707        os.remove(test_abc_path)
708
709        return self
710
711
712class CompilerProjectTest(Test):
713    def __init__(self, projects_path, project, test_paths, flags):
714        Test.__init__(self, "", flags)
715        self.projects_path = projects_path
716        self.project = project
717        self.test_paths = test_paths
718        self.files_info_path = os.path.join(os.path.join(self.projects_path, self.project), 'filesInfo.txt')
719        # Skip execution if --dump-assembly exists in flags
720        self.requires_execution = "--dump-assembly" not in self.flags
721        self.file_record_mapping = None
722        self.generated_abc_inputs_path = os.path.join(os.path.join(self.projects_path, self.project), "abcinputs_gen")
723        self.abc_input_filenames = None
724        self.record_names_path = os.path.join(os.path.join(self.projects_path, self.project), 'recordnames.txt')
725        self.abc_inputs_path = os.path.join(os.path.join(self.projects_path, self.project), 'abcinputs')
726        self.deps_json_path = os.path.join(os.path.join(self.projects_path, self.project), 'deps-json.json')
727
728    def remove_project(self, runner):
729        project_path = runner.build_dir + "/" + self.project
730        if path.exists(project_path):
731            shutil.rmtree(project_path)
732        if path.exists(self.files_info_path):
733            os.remove(self.files_info_path)
734        if path.exists(self.generated_abc_inputs_path):
735            shutil.rmtree(self.generated_abc_inputs_path)
736
737    def get_file_absolute_path_and_name(self, runner):
738        sub_path = self.path[len(self.projects_path):]
739        file_relative_path = path.split(sub_path)[0]
740        file_name = path.split(sub_path)[1]
741        file_absolute_path = runner.build_dir + "/" + file_relative_path
742        return [file_absolute_path, file_name]
743
744    def gen_single_abc(self, runner):
745        for test_path in self.test_paths:
746            self.path = test_path
747            [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner)
748            if not path.exists(file_absolute_path):
749                os.makedirs(file_absolute_path)
750
751            test_abc_name = ("%s.abc" % (path.splitext(file_name)[0]))
752            test_abc_path = path.join(file_absolute_path, test_abc_name)
753            es2abc_cmd = runner.cmd_prefix + [runner.es2panda]
754            es2abc_cmd.extend(self.flags)
755            es2abc_cmd.extend(['%s%s' % ("--output=", test_abc_path)])
756            es2abc_cmd.append(self.path)
757            process = run_subprocess_with_beta3(self, es2abc_cmd)
758            out, err = process.communicate()
759            if err:
760                self.passed = False
761                self.error = err.decode("utf-8", errors="ignore")
762                self.remove_project(runner)
763                return self
764
765    def collect_record_mapping(self):
766        # Collect record mappings from recordnames.txt, file format:
767        # 'source_file_name:record_name\n' * n
768        if path.exists(self.record_names_path):
769            with open(self.record_names_path) as mapping_fp:
770                mapping_lines = mapping_fp.readlines()
771                self.file_record_mapping = {}
772                for mapping_line in mapping_lines:
773                    cur_mapping = mapping_line[:-1].split(":")
774                    self.file_record_mapping[cur_mapping[0]] = cur_mapping[1]
775
776    def get_record_name(self, test_path):
777        record_name = os.path.relpath(test_path, os.path.dirname(self.files_info_path)).split('.')[0]
778        if (self.file_record_mapping is not None and record_name in self.file_record_mapping):
779            record_name = self.file_record_mapping[record_name]
780        return record_name
781
782    def collect_abc_inputs(self, runner):
783        # Collect abc input information from the 'abcinputs' directory. Each txt file in the directory
784        # will generate a merged abc file with the same filename and serve as the final abc input.
785        # file format: 'source_file_name.ts\n' * n
786        if not path.exists(self.abc_inputs_path):
787            return
788        if not path.exists(self.generated_abc_inputs_path):
789            os.makedirs(self.generated_abc_inputs_path)
790        self.abc_input_filenames = {}
791        filenames = os.listdir(self.abc_inputs_path)
792        for filename in filenames:
793            if not filename.endswith('.txt'):
794                self.remove_project(runner)
795                raise Exception("Invalid abc input file: %s, only txt files are allowed in abcinputs directory: %s"
796                                % (filename, self.abc_inputs_path))
797            with open(path.join(self.abc_inputs_path, filename)) as abc_inputs_fp:
798                abc_inputs_lines = abc_inputs_fp.readlines()
799                for abc_input_line in abc_inputs_lines:
800                    # filename is 'xxx.txt', remove '.txt' here
801                    self.abc_input_filenames[abc_input_line[:-1]] = filename[:-len('.txt')]
802
803    def get_belonging_abc_input(self, test_path):
804        filename = os.path.relpath(test_path, os.path.dirname(self.files_info_path))
805        if (self.abc_input_filenames is not None and filename in self.abc_input_filenames):
806            return self.abc_input_filenames[filename]
807        return None
808
809    def gen_abc_input_files_infos(self, runner, abc_files_infos, final_file_info_f):
810        for abc_files_info_name in abc_files_infos:
811            abc_files_info = abc_files_infos[abc_files_info_name]
812            if len(abc_files_info) != 0:
813                abc_input_path = path.join(self.generated_abc_inputs_path, abc_files_info_name)
814                abc_files_info_path = ("%s-filesInfo.txt" % (abc_input_path))
815                abc_files_info_fd = os.open(abc_files_info_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC)
816                abc_files_info_f = os.fdopen(abc_files_info_fd, 'w')
817                abc_files_info_f.writelines(abc_files_info)
818                final_file_info_f.writelines('%s-abcinput.abc;;;;%s;\n' % (abc_input_path, abc_files_info_name))
819
820    def gen_files_info(self, runner):
821        # After collect_record_mapping, self.file_record_mapping stores {'source file name' : 'source file record name'}
822        self.collect_record_mapping()
823        # After collect_abc_inputs, self.abc_input_filenames stores {'source file name' : 'belonging abc input name'}
824        self.collect_abc_inputs(runner)
825
826        fd = os.open(self.files_info_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC)
827        f = os.fdopen(fd, 'w')
828        abc_files_infos = {}
829        for test_path in self.test_paths:
830            record_name = self.get_record_name(test_path)
831            module_kind = 'esm'
832            if (os.path.basename(test_path).startswith("commonjs")):
833                module_kind = 'commonjs'
834            is_shared_module = 'false'
835            if (os.path.basename(test_path).startswith("sharedmodule")):
836                is_shared_module = 'true'
837            file_info = ('%s;%s;%s;%s;%s;%s\n' % (test_path, record_name, module_kind,
838                                               os.path.relpath(test_path, self.projects_path), record_name,
839                                               is_shared_module))
840            belonging_abc_input = self.get_belonging_abc_input(test_path)
841            if belonging_abc_input is not None:
842                if not belonging_abc_input in abc_files_infos:
843                    abc_files_infos[belonging_abc_input] = []
844                abc_files_infos[belonging_abc_input].append(file_info)
845            else:
846                f.writelines(file_info)
847        if (os.path.exists(self.deps_json_path)):
848            record_name = self.get_record_name(self.deps_json_path)
849            file_info = ('%s;%s;%s;%s;%s;%s\n' % (self.deps_json_path, record_name, 'esm',
850                                               os.path.relpath(self.deps_json_path, self.projects_path), record_name,
851                                               'false'))
852        self.gen_abc_input_files_infos(runner, abc_files_infos, f)
853        f.close()
854
855    def gen_es2abc_cmd(self, runner, input_file, output_file):
856        es2abc_cmd = runner.cmd_prefix + [runner.es2panda]
857
858        new_flags = self.flags
859        if "--cache-file" in new_flags and len(self.test_paths) == 1:
860            # Generate cache-file test case in single thread
861            new_flags.remove("--cache-file")
862            protobin_path = f"{self.test_paths[0].rsplit('.', 1)[0]}.protobin"
863            self.protoBin_file_path = protobin_path
864            es2abc_cmd.append('--cache-file=%s' % (protobin_path))
865
866        es2abc_cmd.extend(new_flags)
867        es2abc_cmd.extend(['%s%s' % ("--output=", output_file)])
868        es2abc_cmd.append(input_file)
869        return es2abc_cmd
870
871    def gen_merged_abc_for_abc_input(self, runner, files_info_name):
872        self.passed = True
873        if not files_info_name.endswith(".txt"):
874            return
875        abc_input_files_info_path = path.join(self.generated_abc_inputs_path, files_info_name)
876        abc_input_merged_abc_path = path.join(self.generated_abc_inputs_path,
877                                              '%s-abcinput.abc' % (files_info_name[:-len('-filesInfo.txt')]))
878
879        abc_input_file_path = '@' + abc_input_files_info_path
880        if "unmerged_abc_input" in self.generated_abc_inputs_path:
881            self.flags.remove("--merge-abc")
882            with open(abc_input_files_info_path, 'r') as fp:
883                abc_input_file_path = fp.read().split(';')[0]
884
885        es2abc_cmd = self.gen_es2abc_cmd(runner, abc_input_file_path, abc_input_merged_abc_path)
886        process = run_subprocess_with_beta3(self, es2abc_cmd)
887        out, err = process.communicate()
888        if err:
889            self.passed = False
890            self.error = err.decode("utf-8", errors="ignore")
891
892    def gen_merged_abc(self, runner):
893        # Generate abc inputs
894        if (os.path.exists(self.generated_abc_inputs_path)):
895            files_info_names = os.listdir(self.generated_abc_inputs_path)
896            for filename in files_info_names:
897                self.gen_merged_abc_for_abc_input(runner, filename)
898                if (not self.passed):
899                    self.remove_project(runner)
900                    return self
901        # Generate the abc to be tested
902        for test_path in self.test_paths:
903            self.path = test_path
904            if (self.path.endswith("-exec.ts")) or (self.path.endswith("-exec.js")):
905                exec_file_path = self.path
906                [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner)
907                if not path.exists(file_absolute_path):
908                    os.makedirs(file_absolute_path)
909                test_abc_name = ("%s.abc" % (path.splitext(file_name)[0]))
910                output_abc_name = path.join(file_absolute_path, test_abc_name)
911
912        # reverse merge-abc flag
913        if "merge_abc_consistence_check" in self.path:
914            if "--merge-abc" in self.flags:
915                self.flags.remove("--merge-abc")
916            else:
917                self.flags.append("--merge-abc")
918
919        es2abc_cmd = self.gen_es2abc_cmd(runner, '@' + self.files_info_path, output_abc_name)
920        compile_context_info_path = path.join(path.join(self.projects_path, self.project), "compileContextInfo.json")
921        if path.exists(compile_context_info_path):
922            es2abc_cmd.append("%s%s" % ("--compile-context-info=", compile_context_info_path))
923        process = run_subprocess_with_beta3(self, es2abc_cmd)
924        self.path = exec_file_path
925        out, err = [None, None]
926
927        # Check single-thread execution timeout when required
928        if "--file-threads=0" in self.flags:
929            try:
930                out, err = process.communicate(timeout=60)
931            except:
932                process.kill()
933                print("Generating the abc file timed out.")
934        else:
935            out, err = process.communicate()
936
937        # restore merge-abc flag
938        if "merge_abc_consistence_check" in self.path and "--merge-abc" not in self.flags:
939            self.flags.append("--merge-abc")
940
941        # Check dump-assembly outputs when required
942        if "--dump-assembly" in self.flags:
943            pa_expected_path = "".join([self.get_path_to_expected()[:self.get_path_to_expected().rfind(".txt")],
944                                        ".pa.txt"])
945            self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
946            if "merge_abc_consistence_check" in self.path:
947                self.output = self.output.split('.')[0]
948            try:
949                with open(pa_expected_path, 'r') as fp:
950                    expected = fp.read()
951                self.passed = expected == self.output and process.returncode in [0, 1]
952            except Exception:
953                self.passed = False
954            if not self.passed:
955                self.error = err.decode("utf-8", errors="ignore")
956                self.remove_project(runner)
957                return self
958            else:
959                return self
960
961        if err:
962            self.passed = False
963            self.error = err.decode("utf-8", errors="ignore")
964            self.remove_project(runner)
965            return self
966
967    def run(self, runner):
968        # Compile all ts source files in the project to abc files.
969        if ("--merge-abc" in self.flags):
970            self.gen_files_info(runner)
971            self.gen_merged_abc(runner)
972        else:
973            self.gen_single_abc(runner)
974
975        if (not self.requires_execution):
976            self.remove_project(runner)
977            return self
978
979        # Run test files that need to be executed in the project.
980        for test_path in self.test_paths:
981            self.path = test_path
982            if self.path.endswith("-exec.ts"):
983                [file_absolute_path, file_name] = self.get_file_absolute_path_and_name(runner)
984
985                entry_point_name = path.splitext(file_name)[0]
986                test_abc_name = ("%s.abc" % entry_point_name)
987                test_abc_path = path.join(file_absolute_path, test_abc_name)
988
989                ld_library_path = runner.ld_library_path
990                os.environ.setdefault("LD_LIBRARY_PATH", ld_library_path)
991                run_abc_cmd = [runner.ark_js_vm]
992                if ("--merge-abc" in self.flags):
993                    run_abc_cmd.extend(["--entry-point", entry_point_name])
994                run_abc_cmd.extend([test_abc_path])
995                self.log_cmd(run_abc_cmd)
996
997                process = subprocess.Popen(run_abc_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
998                out, err = process.communicate()
999                self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
1000                expected_path = self.get_path_to_expected()
1001                try:
1002                    with open(expected_path, 'r') as fp:
1003                        expected = fp.read()
1004                    self.passed = expected == self.output and process.returncode in [0, 1]
1005                except Exception:
1006                    self.passed = False
1007
1008                if not self.passed:
1009                    self.error = err.decode("utf-8", errors="ignore")
1010                    self.remove_project(runner)
1011                    return self
1012
1013            self.passed = True
1014
1015        self.remove_project(runner)
1016        return self
1017
1018
1019class TSDeclarationTest(Test):
1020    def get_path_to_expected(self):
1021        file_name = self.path[:self.path.find(".d.ts")]
1022        return "%s-expected.txt" % file_name
1023
1024
1025class BcVersionRunner(Runner):
1026    def __init__(self, args):
1027        Runner.__init__(self, args, "Target bc version")
1028        self.ts2abc = path.join(self.test_root, '..', 'scripts', 'ts2abc.js')
1029
1030    def add_cmd(self):
1031        api_sub_version_list = ["beta1", "beta2", "beta3"]
1032        for api_version in range(8, 14):
1033            cmd = self.cmd_prefix + [self.es2panda]
1034            cmd += ["--target-bc-version"]
1035            cmd += ["--target-api-version"]
1036            cmd += [str(api_version)]
1037            self.tests += [BcVersionTest(cmd, api_version)]
1038            node_cmd = ["node"] + [self.ts2abc]
1039            node_cmd += ["".join(["es2abc=", self.es2panda])]
1040            node_cmd += ["--target-api-version"]
1041            node_cmd += [str(api_version)]
1042            self.tests += [BcVersionTest(node_cmd, api_version)]
1043
1044            # Add tests for "--target-api-sub-version" option
1045            if api_version == 12:
1046                for api_sub_version in api_sub_version_list:
1047                    new_cmd = cmd.copy()
1048                    new_cmd += ["--target-api-sub-version", api_sub_version]
1049                    self.tests += [BcVersionTest(new_cmd, str(api_version) + '_' + api_sub_version)]
1050                    new_node_cmd = node_cmd.copy()
1051                    new_node_cmd += ["--target-api-sub-version", api_sub_version]
1052                    self.tests += [BcVersionTest(new_node_cmd, str(api_version) + '_' + api_sub_version)]
1053
1054    def run(self):
1055        for test in self.tests:
1056            test.run()
1057
1058
1059class BcVersionTest(Test):
1060    def __init__(self, cmd, api_version):
1061        Test.__init__(self, "", 0)
1062        self.cmd = cmd
1063        self.api_version = api_version
1064        self.bc_version_expect = {
1065            8: "12.0.6.0",
1066            9: "9.0.0.0",
1067            10: "9.0.0.0",
1068            11: "11.0.2.0",
1069            12: "12.0.2.0",
1070            "12_beta1": "12.0.2.0",
1071            "12_beta2": "12.0.2.0",
1072            "12_beta3": "12.0.6.0",
1073            13: "12.0.6.0"
1074        }
1075        self.es2abc_script_expect = {
1076            8: "0.0.0.2",
1077            9: "9.0.0.0",
1078            10: "9.0.0.0",
1079            11: "11.0.2.0",
1080            12: "12.0.2.0",
1081            "12_beta1": "12.0.2.0",
1082            "12_beta2": "12.0.2.0",
1083            "12_beta3": "12.0.6.0",
1084            13: "12.0.6.0"
1085        }
1086
1087    def run(self):
1088        process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1089        out, err = process.communicate()
1090        self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
1091        if self.cmd[0] == "node":
1092            self.passed = self.es2abc_script_expect.get(self.api_version) == self.output and process.returncode in [0, 1]
1093        else:
1094            self.passed = self.bc_version_expect.get(self.api_version) == self.output and process.returncode in [0, 1]
1095        if not self.passed:
1096            self.error = err.decode("utf-8", errors="ignore")
1097        return self
1098
1099
1100class TransformerRunner(Runner):
1101    def __init__(self, args):
1102        Runner.__init__(self, args, "Transformer")
1103
1104    def add_directory(self, directory, extension, flags):
1105        glob_expression = path.join(
1106            self.test_root, directory, "**/*.%s" % (extension))
1107        files = glob(glob_expression, recursive=True)
1108        files = fnmatch.filter(files, self.test_root + '**' + self.args.filter)
1109
1110        self.tests += list(map(lambda f: TransformerTest(f, flags), files))
1111
1112    def test_path(self, src):
1113        return src
1114
1115
1116class TransformerInTargetApiVersion10Runner(Runner):
1117    def __init__(self, args):
1118        Runner.__init__(self, args, "TransformerInTargetApiVersion10")
1119
1120    def add_directory(self, directory, extension, flags):
1121        glob_expression = path.join(
1122            self.test_root, directory, "**/*.%s" % (extension))
1123        files = glob(glob_expression, recursive=True)
1124        files = fnmatch.filter(files, self.test_root + '**' + self.args.filter)
1125
1126        self.tests += list(map(lambda f: TransformerTest(f, flags), files))
1127
1128    def test_path(self, src):
1129        return src
1130
1131
1132class TransformerTest(Test):
1133    def __init__(self, test_path, flags):
1134        Test.__init__(self, test_path, flags)
1135
1136    def get_path_to_expected(self):
1137        return "%s-transformed-expected.txt" % (path.splitext(self.path)[0])
1138
1139    def run(self, runner):
1140        cmd = runner.cmd_prefix + [runner.es2panda]
1141        cmd.extend(self.flags)
1142        cmd.append(self.path)
1143        process = run_subprocess_with_beta3(self, cmd)
1144        out, err = process.communicate()
1145        self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore")
1146
1147        expected_path = self.get_path_to_expected()
1148        try:
1149            with open(expected_path, 'r') as fp:
1150                expected = fp.read()
1151            self.passed = expected == self.output and process.returncode in [0, 1]
1152        except Exception:
1153            self.passed = False
1154
1155        if not self.passed:
1156            self.error = err.decode("utf-8", errors="ignore")
1157
1158        return self
1159
1160
1161class PatchTest(Test):
1162    def __init__(self, test_path, mode_arg, target_version, preserve_files):
1163        Test.__init__(self, test_path, "")
1164        self.mode = mode_arg
1165        self.target_version = target_version
1166        self.preserve_files = preserve_files
1167
1168    def gen_cmd(self, runner):
1169        symbol_table_file = os.path.join(self.path, 'base.map')
1170        origin_input_file = 'base.js'
1171        origin_output_abc = os.path.join(self.path, 'base.abc')
1172        modified_input_file = 'base_mod.js'
1173        modified_output_abc = os.path.join(self.path, 'patch.abc')
1174        target_version_cmd = ""
1175        if self.target_version > 0:
1176            target_version_cmd = "--target-api-version=" + str(self.target_version)
1177
1178        gen_base_cmd = runner.cmd_prefix + [runner.es2panda, '--module', target_version_cmd]
1179        if 'record-name-with-dots' in os.path.basename(self.path):
1180            gen_base_cmd.extend(['--merge-abc', '--record-name=record.name.with.dots'])
1181        gen_base_cmd.extend(['--dump-symbol-table', symbol_table_file])
1182        gen_base_cmd.extend(['--output', origin_output_abc])
1183        gen_base_cmd.extend([os.path.join(self.path, origin_input_file)])
1184        self.log_cmd(gen_base_cmd)
1185
1186        if self.mode == 'hotfix':
1187            mode_arg = ["--generate-patch"]
1188        elif self.mode == 'hotreload':
1189            mode_arg = ["--hot-reload"]
1190        elif self.mode == 'coldfix':
1191            mode_arg = ["--generate-patch", "--cold-fix"]
1192        elif self.mode == 'coldreload':
1193            mode_arg = ["--cold-reload"]
1194
1195        patch_test_cmd = runner.cmd_prefix + [runner.es2panda, '--module', target_version_cmd]
1196        patch_test_cmd.extend(mode_arg)
1197        patch_test_cmd.extend(['--input-symbol-table', symbol_table_file])
1198        patch_test_cmd.extend(['--output', modified_output_abc])
1199        patch_test_cmd.extend([os.path.join(self.path, modified_input_file)])
1200        if 'record-name-with-dots' in os.path.basename(self.path):
1201            patch_test_cmd.extend(['--merge-abc', '--record-name=record.name.with.dots'])
1202        dump_assembly_testname = [
1203            'modify-anon-content-keep-origin-name',
1204            'modify-class-memeber-function',
1205            'exist-lexenv-3',
1206            'lexenv-reduce',
1207            'lexenv-increase']
1208        for name in dump_assembly_testname:
1209            if name in os.path.basename(self.path):
1210                patch_test_cmd.extend(['--dump-assembly'])
1211        self.log_cmd(patch_test_cmd)
1212
1213        return gen_base_cmd, patch_test_cmd, symbol_table_file, origin_output_abc, modified_output_abc
1214
1215    def run(self, runner):
1216        gen_base_cmd, patch_test_cmd, symbol_table_file, origin_output_abc, modified_output_abc = self.gen_cmd(runner)
1217
1218        process_base = run_subprocess_with_beta3(None, gen_base_cmd)
1219        stdout_base, stderr_base = process_base.communicate(timeout=runner.args.es2panda_timeout)
1220        if stderr_base:
1221            self.passed = False
1222            self.error = stderr_base.decode("utf-8", errors="ignore")
1223            self.output = stdout_base.decode("utf-8", errors="ignore") + stderr_base.decode("utf-8", errors="ignore")
1224        else:
1225            process_patch = run_subprocess_with_beta3(None, patch_test_cmd)
1226            process_patch = subprocess.Popen(patch_test_cmd, stdout=subprocess.PIPE,
1227                stderr=subprocess.PIPE)
1228            stdout_patch, stderr_patch = process_patch.communicate(timeout=runner.args.es2panda_timeout)
1229            if stderr_patch:
1230                self.passed = False
1231                self.error = stderr_patch.decode("utf-8", errors="ignore")
1232            self.output = stdout_patch.decode("utf-8", errors="ignore") + stderr_patch.decode("utf-8", errors="ignore")
1233
1234        expected_path = os.path.join(self.path, 'expected.txt')
1235        try:
1236            with open(expected_path, 'r') as fp:
1237                # ignore license description lines and skip leading blank lines
1238                expected = (''.join((fp.readlines()[12:]))).lstrip()
1239            self.passed = expected == self.output
1240        except Exception:
1241            self.passed = False
1242
1243        if not self.passed:
1244            self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\
1245                self.output
1246        if not self.preserve_files:
1247            os.remove(symbol_table_file)
1248            os.remove(origin_output_abc)
1249            if (os.path.exists(modified_output_abc)):
1250                os.remove(modified_output_abc)
1251        return self
1252
1253
1254class PatchRunner(Runner):
1255    def __init__(self, args, name):
1256        Runner.__init__(self, args, name)
1257        self.preserve_files = args.error
1258        self.tests_in_dirs = []
1259        dirs = os.listdir(path.join(self.test_root, "patch"))
1260        for target_version_path in dirs:
1261            self.add_tests(target_version_path, name)
1262
1263    def add_tests(self, target_version_path, name):
1264        name_dir = os.path.join(self.test_root, "patch", target_version_path, name)
1265        if not os.path.exists(name_dir):
1266            return
1267        target_version = 0
1268        if target_version_path.isdigit():
1269            target_version = int(target_version_path)
1270        for sub_path in os.listdir(name_dir):
1271            test_base_path = os.path.join(name_dir, sub_path)
1272            if name != "coldreload":
1273                for test_dir in os.listdir(test_base_path):
1274                    test_path = os.path.join(test_base_path, test_dir)
1275                    self.tests_in_dirs.append(test_path)
1276                    self.tests.append(PatchTest(test_path, name, target_version, self.preserve_files))
1277            else:
1278                self.tests_in_dirs.append(test_base_path)
1279                self.tests.append(PatchTest(test_base_path, name, target_version, self.preserve_files))
1280
1281    def test_path(self, src):
1282        return os.path.basename(src)
1283
1284
1285class HotfixRunner(PatchRunner):
1286    def __init__(self, args):
1287        PatchRunner.__init__(self, args, "hotfix")
1288
1289
1290class HotreloadRunner(PatchRunner):
1291    def __init__(self, args):
1292        PatchRunner.__init__(self, args, "hotreload")
1293
1294
1295class ColdfixRunner(PatchRunner):
1296    def __init__(self, args):
1297        PatchRunner.__init__(self, args, "coldfix")
1298
1299
1300class ColdreloadRunner(PatchRunner):
1301    def __init__(self, args):
1302        PatchRunner.__init__(self, args, "coldreload")
1303
1304
1305class DebuggerTest(Test):
1306    def __init__(self, test_path, mode):
1307        Test.__init__(self, test_path, "")
1308        self.mode = mode
1309
1310    def run(self, runner):
1311        cmd = runner.cmd_prefix + [runner.es2panda, "--module"]
1312        input_file_name = 'base.js'
1313        if self.mode == "debug-mode":
1314            cmd.extend(['--debug-info'])
1315        cmd.extend([os.path.join(self.path, input_file_name)])
1316        cmd.extend(['--dump-assembly'])
1317        process = run_subprocess_with_beta3(self, cmd)
1318        stdout, stderr = process.communicate(timeout=runner.args.es2panda_timeout)
1319        if stderr:
1320            self.passed = False
1321            self.error = stderr.decode("utf-8", errors="ignore")
1322            return self
1323
1324        self.output = stdout.decode("utf-8", errors="ignore")
1325
1326        expected_path = os.path.join(self.path, 'expected.txt')
1327        try:
1328            with open(expected_path, 'r') as fp:
1329                expected = (''.join((fp.readlines()[12:]))).lstrip()
1330            self.passed = expected == self.output
1331        except Exception:
1332            self.passed = False
1333
1334        if not self.passed:
1335            self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\
1336                self.output
1337
1338        if os.path.exists("base.abc"):
1339            os.remove("base.abc")
1340
1341        return self
1342
1343
1344class DebuggerRunner(Runner):
1345    def __init__(self, args):
1346        Runner.__init__(self, args, "debugger")
1347        self.test_directory = path.join(self.test_root, "debugger")
1348        self.add_test()
1349
1350    def add_test(self):
1351        self.tests = []
1352        self.tests.append(DebuggerTest(os.path.join(self.test_directory, "debugger-in-debug"), "debug-mode"))
1353        self.tests.append(DebuggerTest(os.path.join(self.test_directory, "debugger-in-release"), "release-mode"))
1354
1355
1356class Base64Test(Test):
1357    def __init__(self, test_path, input_type):
1358        Test.__init__(self, test_path, "")
1359        self.input_type = input_type
1360
1361    def run(self, runner):
1362        cmd = runner.cmd_prefix + [runner.es2panda, "--base64Output"]
1363        if self.input_type == "file":
1364            input_file_name = 'input.js'
1365            cmd.extend(['--source-file', input_file_name])
1366            cmd.extend([os.path.join(self.path, input_file_name)])
1367        elif self.input_type == "string":
1368            input_file = os.path.join(self.path, "input.txt")
1369            try:
1370                with open(input_file, 'r') as fp:
1371                    base64_input = (''.join((fp.readlines()[12:]))).lstrip()  # ignore license description lines
1372                    cmd.extend(["--base64Input", base64_input])
1373            except Exception:
1374                self.passed = False
1375        elif self.input_type == "targetApiVersion":
1376            # base64 test for all available target api version.
1377            version = os.path.basename(self.path)
1378            cmd.extend(['--target-api-version', version])
1379            if version == "12":
1380                cmd.append("--target-api-sub-version=beta3")
1381            input_file = os.path.join(self.path, "input.txt")
1382            try:
1383                with open(input_file, 'r') as fp:
1384                    base64_input = (''.join((fp.readlines()[12:]))).lstrip()  # ignore license description lines
1385                    cmd.extend(["--base64Input", base64_input])
1386            except Exception:
1387                self.passed = False
1388        else:
1389            self.error = "Unsupported base64 input type"
1390            self.passed = False
1391            return self
1392
1393        version = os.path.basename(self.path)
1394        if not self.input_type == "targetApiVersion":
1395            cmd.append("--target-api-sub-version=beta3")
1396
1397        self.log_cmd(cmd)
1398
1399        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1400        stdout, stderr = process.communicate(timeout=runner.args.es2panda_timeout)
1401        if stderr:
1402            self.passed = False
1403            self.error = stderr.decode("utf-8", errors="ignore")
1404            return self
1405
1406        self.output = stdout.decode("utf-8", errors="ignore")
1407
1408        expected_path = os.path.join(self.path, 'expected.txt')
1409        try:
1410            with open(expected_path, 'r') as fp:
1411                expected = (''.join((fp.readlines()[12:]))).lstrip()
1412            self.passed = expected == self.output
1413        except Exception:
1414            self.passed = False
1415
1416        if not self.passed:
1417            self.error = "expected output:" + os.linesep + expected + os.linesep + "actual output:" + os.linesep +\
1418                self.output
1419
1420        return self
1421
1422
1423class Base64Runner(Runner):
1424    def __init__(self, args):
1425        Runner.__init__(self, args, "Base64")
1426        self.test_directory = path.join(self.test_root, "base64")
1427        self.add_test()
1428
1429    def add_test(self):
1430        self.tests = []
1431        self.tests.append(Base64Test(os.path.join(self.test_directory, "inputFile"), "file"))
1432        self.tests.append(Base64Test(os.path.join(self.test_directory, "inputString"), "string"))
1433        # current target api version is 12, once a new version is addded, a new testcase should be added here.
1434        current_version = 12
1435        available_target_api_versions = [9, 10, 11, current_version]
1436        for version in available_target_api_versions:
1437            self.tests.append(Base64Test(os.path.join(self.test_directory, "availableTargetApiVersion", str(version)),
1438                "targetApiVersion"))
1439
1440    def test_path(self, src):
1441        return os.path.basename(src)
1442
1443
1444class BytecodeRunner(Runner):
1445    def __init__(self, args):
1446        Runner.__init__(self, args, "Bytecode")
1447
1448    def add_directory(self, directory, extension, flags, func=Test):
1449        glob_expression = path.join(
1450            self.test_root, directory, "**/*.%s" % (extension))
1451        files = glob(glob_expression, recursive=True)
1452        files = fnmatch.filter(files, self.test_root + '**' + self.args.filter)
1453        self.tests += list(map(lambda f: func(f, flags), files))
1454
1455    def test_path(self, src):
1456        return src
1457
1458
1459class CompilerTestInfo(object):
1460    def __init__(self, directory, extension, flags):
1461        self.directory = directory
1462        self.extension = extension
1463        self.flags = flags
1464
1465    def update_dir(self, prefiex_dir):
1466        self.directory = os.path.sep.join([prefiex_dir, self.directory])
1467
1468
1469# Copy compiler directory to test/.local directory, and do inplace obfuscation.
1470def prepare_for_obfuscation(compiler_test_infos, test_root):
1471    tmp_dir_name = ".local"
1472    tmp_path = os.path.join(test_root, tmp_dir_name)
1473    if not os.path.exists(tmp_path):
1474        os.mkdir(tmp_path)
1475
1476    test_root_dirs = set()
1477    for info in compiler_test_infos:
1478        root_dir = info.directory.split("/")[0]
1479        test_root_dirs.add(root_dir)
1480
1481    for test_dir in test_root_dirs:
1482        src_dir = os.path.join(test_root, test_dir)
1483        target_dir = os.path.join(tmp_path, test_dir)
1484        if os.path.exists(target_dir):
1485            shutil.rmtree(target_dir)
1486        shutil.copytree(src_dir, target_dir)
1487
1488    for info in compiler_test_infos:
1489        info.update_dir(tmp_dir_name)
1490
1491
1492def add_directory_for_regression(runners, args):
1493    runner = RegressionRunner(args)
1494    runner.add_directory("parser/concurrent", "js", ["--module", "--dump-ast"])
1495    runner.add_directory("parser/js", "js", ["--parse-only", "--dump-ast"])
1496    runner.add_directory("parser/script", "ts", ["--parse-only", "--dump-ast"])
1497    runner.add_directory("parser/ts", "ts",
1498                         ["--parse-only", "--module", "--dump-ast"])
1499    runner.add_directory("parser/ts/type_checker", "ts",
1500                         ["--parse-only", "--enable-type-check", "--module", "--dump-ast"])
1501    runner.add_directory("parser/ts/cases/declaration", "d.ts",
1502                         ["--parse-only", "--module", "--dump-ast"], TSDeclarationTest)
1503    runner.add_directory("parser/commonjs", "js", ["--commonjs", "--parse-only", "--dump-ast"])
1504    runner.add_directory("parser/binder", "js", ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"])
1505    runner.add_directory("parser/binder", "ts", ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"])
1506    runner.add_directory("parser/binder/noModule", "ts", ["--dump-assembly", "--dump-literal-buffer", "--target-api-sub-version=beta3"])
1507    runner.add_directory("parser/binder/api12beta2", "js", ["--dump-assembly", "--target-api-version=12", "--target-api-sub-version=beta2"])
1508    runner.add_directory("parser/binder/debugInfo", "ts", ["--dump-assembly", "--dump-literal-buffer", "--debug-info", "--module"])
1509    runner.add_directory("parser/js/emptySource", "js", ["--dump-assembly"])
1510    runner.add_directory("parser/js/language/arguments-object", "js", ["--parse-only"])
1511    runner.add_directory("parser/js/language/statements/for-statement", "js", ["--parse-only", "--dump-ast"])
1512    runner.add_directory("parser/js/language/expressions/optional-chain", "js", ["--parse-only", "--dump-ast"])
1513    runner.add_directory("parser/js/language/import/syntax", "js",
1514                         ["--parse-only", "--module", "--target-api-sub-version=beta3"])
1515    runner.add_directory("parser/js/language/import/syntax/beta2", "js",
1516                         ["--parse-only", "--module", "--target-api-version=12", "--target-api-sub-version=beta2"])
1517    runner.add_directory("parser/js/language/import", "ts",
1518                         ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"])
1519    runner.add_directory("parser/sendable_class", "ts",
1520                         ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-sub-version=beta3"])
1521    runner.add_directory("parser/sendable_class/api12beta2", "ts",
1522                         ["--dump-assembly", "--dump-literal-buffer", "--module", "--target-api-version=12", "--target-api-sub-version=beta2"])
1523    runner.add_directory("parser/unicode", "js", ["--parse-only"])
1524    runner.add_directory("parser/ts/stack_overflow", "ts", ["--parse-only", "--dump-ast"])
1525    runner.add_directory("parser/js/module-record/module-record-field-name-option.js", "js",
1526                         ["--module-record-field-name=abc", "--source-file=abc", "--module", "--dump-normalized-asm-program"])
1527
1528    runners.append(runner)
1529
1530    transformer_runner = TransformerRunner(args)
1531    transformer_runner.add_directory("parser/ts/transformed_cases", "ts",
1532                                     ["--parse-only", "--module", "--dump-transformed-ast",
1533                                     "--check-transformed-ast-structure"])
1534
1535    runners.append(transformer_runner)
1536
1537    bc_version_runner = BcVersionRunner(args)
1538    bc_version_runner.add_cmd()
1539
1540    runners.append(bc_version_runner)
1541
1542    transformer_api_version_10_runner = TransformerInTargetApiVersion10Runner(args)
1543    transformer_api_version_10_runner.add_directory("parser/ts/transformed_cases_api_version_10", "ts",
1544                                                    ["--parse-only", "--module", "--target-api-version=10",
1545                                                    "--dump-transformed-ast"])
1546
1547    runners.append(transformer_api_version_10_runner)
1548
1549def add_directory_for_asm(runners, args, mode = ""):
1550    runner = AbcToAsmRunner(args, True if mode == "debug" else False)
1551    runner.add_directory("abc2asm/js", "js", [])
1552    runner.add_directory("abc2asm/ts", "ts", [])
1553    runner.add_directory("compiler/js", "js", [])
1554    runner.add_directory("compiler/ts/cases/compiler", "ts", [])
1555    runner.add_directory("compiler/ts/projects", "ts", ["--module"])
1556    runner.add_directory("compiler/ts/projects", "ts", ["--module", "--merge-abc"])
1557    runner.add_directory("compiler/dts", "d.ts", ["--module", "--opt-level=0"])
1558    runner.add_directory("compiler/commonjs", "js", ["--commonjs"])
1559    runner.add_directory("parser/concurrent", "js", ["--module"])
1560    runner.add_directory("parser/js", "js", [])
1561    runner.add_directory("parser/script", "ts", [])
1562    runner.add_directory("parser/ts", "ts", ["--module"])
1563    runner.add_directory("parser/ts/type_checker", "ts", ["--enable-type-check", "--module"])
1564    runner.add_directory("parser/commonjs", "js", ["--commonjs"])
1565    runner.add_directory("parser/binder", "js", ["--dump-assembly", "--dump-literal-buffer", "--module"])
1566    runner.add_directory("parser/binder", "ts", ["--dump-assembly", "--dump-literal-buffer", "--module"])
1567    runner.add_directory("parser/binder/noModule", "ts", ["--dump-assembly", "--dump-literal-buffer"])
1568    runner.add_directory("parser/js/emptySource", "js", [])
1569    runner.add_directory("parser/js/language/arguments-object", "js", [])
1570    runner.add_directory("parser/js/language/statements/for-statement", "js", [])
1571    runner.add_directory("parser/js/language/expressions/optional-chain", "js", [])
1572    runner.add_directory("parser/sendable_class", "ts", ["--module"])
1573    runner.add_directory("parser/unicode", "js", [])
1574    runner.add_directory("parser/ts/stack_overflow", "ts", [])
1575
1576    runners.append(runner)
1577
1578
1579def add_directory_for_compiler(runners, args):
1580    runner = CompilerRunner(args)
1581    compiler_test_infos = []
1582    compiler_test_infos.append(CompilerTestInfo("compiler/js", "js", ["--module"]))
1583    compiler_test_infos.append(CompilerTestInfo("compiler/ts/cases", "ts", []))
1584    compiler_test_infos.append(CompilerTestInfo("compiler/ts/projects", "ts", ["--module"]))
1585    compiler_test_infos.append(CompilerTestInfo("compiler/ts/projects", "ts", ["--module", "--merge-abc"]))
1586    compiler_test_infos.append(CompilerTestInfo("compiler/dts", "d.ts", ["--module", "--opt-level=0"]))
1587    compiler_test_infos.append(CompilerTestInfo("compiler/commonjs", "js", ["--commonjs"]))
1588    compiler_test_infos.append(CompilerTestInfo("compiler/interpreter/lexicalEnv", "js", []))
1589    compiler_test_infos.append(CompilerTestInfo("compiler/sendable", "ts", ["--module", "--target-api-sub-version=beta3"]))
1590    compiler_test_infos.append(CompilerTestInfo("optimizer/js/branch-elimination", "js",
1591                                                ["--module", "--branch-elimination", "--dump-assembly"]))
1592    compiler_test_infos.append(CompilerTestInfo("optimizer/js/opt-try-catch-func", "js",
1593                                                ["--module", "--dump-assembly"]))
1594    compiler_test_infos.append(CompilerTestInfo("compiler/debugInfo/", "js",
1595                                                ["--debug-info", "--dump-debug-info", "--source-file", "debug-info.js"]))
1596    compiler_test_infos.append(CompilerTestInfo("compiler/js/module-record-field-name-option.js", "js",
1597                                                ["--module", "--module-record-field-name=abc"]))
1598    compiler_test_infos.append(CompilerTestInfo("compiler/generateCache-projects", "ts",
1599                                                ["--merge-abc", "--file-threads=0", "--cache-file"]))
1600    # Following directories of test cases are for dump-assembly comparison only, and is not executed.
1601    # Check CompilerProjectTest for more details.
1602    compiler_test_infos.append(CompilerTestInfo("optimizer/ts/branch-elimination/projects", "ts",
1603                                                ["--module", "--branch-elimination", "--merge-abc", "--dump-assembly",
1604                                                "--file-threads=8"]))
1605    compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/projects", "ts",
1606                                                ["--merge-abc", "--dump-assembly", "--enable-abc-input",
1607                                                 "--dump-deps-info", "--remove-redundant-file",
1608                                                 "--dump-literal-buffer", "--dump-string", "--abc-class-threads=4"]))
1609    compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/js/projects", "js",
1610                                                ["--merge-abc", "--dump-assembly", "--enable-abc-input",
1611                                                 "--dump-deps-info", "--remove-redundant-file",
1612                                                 "--dump-literal-buffer", "--dump-string", "--abc-class-threads=4"]))
1613    compiler_test_infos.append(CompilerTestInfo("compiler/bytecodehar/merge_abc_consistence_check/projects", "js",
1614                                                ["--merge-abc", "--dump-assembly", "--enable-abc-input",
1615                                                 "--abc-class-threads=4"]))
1616
1617    compiler_test_infos.append(CompilerTestInfo("compiler/ts/shared_module/projects", "ts",
1618                                                ["--module", "--merge-abc", "--dump-assembly"]))
1619
1620    if args.enable_arkguard:
1621        prepare_for_obfuscation(compiler_test_infos, runner.test_root)
1622
1623    for info in compiler_test_infos:
1624        runner.add_directory(info.directory, info.extension, info.flags)
1625
1626    runners.append(runner)
1627
1628
1629def add_directory_for_bytecode(runners, args):
1630    runner = BytecodeRunner(args)
1631    runner.add_directory("bytecode/commonjs", "js", ["--commonjs", "--dump-assembly"])
1632    runner.add_directory("bytecode/js", "js", ["--dump-assembly"])
1633    runner.add_directory("bytecode/ts/cases", "ts", ["--dump-assembly"])
1634    runner.add_directory("bytecode/ts/ic", "ts", ["--dump-assembly"])
1635    runner.add_directory("bytecode/ts/api11", "ts", ["--dump-assembly", "--module", "--target-api-version=11"])
1636    runner.add_directory("bytecode/ts/api12", "ts", ["--dump-assembly", "--module", "--target-api-version=12"])
1637    runner.add_directory("bytecode/watch-expression", "js", ["--debugger-evaluate-expression", "--dump-assembly"])
1638
1639    runners.append(runner)
1640
1641
1642def add_directory_for_debug(runners, args):
1643    runner = RegressionRunner(args)
1644    runner.add_directory("debug/parser", "js", ["--parse-only", "--dump-ast"])
1645
1646    runners.append(runner)
1647
1648
1649def add_cmd_for_aop_transform(runners, args):
1650    runner = AopTransform(args)
1651
1652    aop_file_path = path.join(runner.test_root, "aop")
1653    lib_suffix = '.so'
1654    #cpp src, deal type, result compare str, abc compare str
1655    msg_list = [
1656        ["correct_modify.cpp", "compile", "aop_transform_start", "new_abc_content"],
1657        ["correct_no_modify.cpp", "compile", "aop_transform_start", ""],
1658        ["exec_error.cpp", "compile", "Transform exec fail", ""],
1659        ["no_func_transform.cpp", "compile", "os::library_loader::ResolveSymbol get func Transform error", ""],
1660        ["error_format.cpp", "copy_lib", "os::library_loader::Load error", ""],
1661        ["".join(["no_exist", lib_suffix]), "dirct_use", "Failed to find file", ""],
1662        ["error_suffix.xxx", "direct_use", "aop transform file suffix support", ""]
1663    ]
1664    for msg in msg_list:
1665        cpp_file = path.join(aop_file_path, msg[0])
1666        if msg[1] == 'compile':
1667            lib_file = cpp_file.replace('.cpp', lib_suffix)
1668            remove_file = lib_file
1669            runner.add_cmd(["g++", "--share", "-o", lib_file, cpp_file], "", "", "")
1670        elif msg[1] == 'copy_lib':
1671            lib_file = cpp_file.replace('.cpp', lib_suffix)
1672            remove_file = lib_file
1673            if not os.path.exists(lib_file):
1674                with open(cpp_file, "r") as source_file:
1675                    fd = os.open(lib_file, os.O_RDWR | os.O_CREAT | os.O_TRUNC)
1676                    target_file = os.fdopen(fd, 'w')
1677                    target_file.write(source_file.read())
1678        elif msg[1] == 'direct_use':
1679            lib_file = cpp_file
1680            remove_file = ""
1681
1682        js_file = path.join(aop_file_path, "test_aop.js")
1683        runner.add_cmd([runner.es2panda, "--merge-abc", "--transform-lib", lib_file, js_file], msg[2], msg[3], remove_file)
1684
1685    runners.append(runner)
1686
1687
1688class AopTransform(Runner):
1689    def __init__(self, args):
1690        Runner.__init__(self, args, "AopTransform")
1691
1692    def add_cmd(self, cmd, compare_str, compare_abc_str, remove_file, func=TestAop):
1693        self.tests += [func(cmd, compare_str, compare_abc_str, remove_file)]
1694
1695    def test_path(self, src):
1696        return src
1697
1698
1699def main():
1700    args = get_args()
1701
1702    runners = []
1703
1704    if args.regression:
1705        add_directory_for_regression(runners, args)
1706
1707    if args.abc_to_asm:
1708        add_directory_for_asm(runners, args)
1709        add_directory_for_asm(runners, args, "debug")
1710
1711    if args.tsc:
1712        runners.append(TSCRunner(args))
1713
1714    if args.compiler:
1715        add_directory_for_compiler(runners, args)
1716
1717    if args.hotfix:
1718        runners.append(HotfixRunner(args))
1719
1720    if args.hotreload:
1721        runners.append(HotreloadRunner(args))
1722
1723    if args.coldfix:
1724        runners.append(ColdfixRunner(args))
1725
1726    if args.coldreload:
1727        runners.append(ColdreloadRunner(args))
1728
1729    if args.debugger:
1730        runners.append(DebuggerRunner(args))
1731
1732    if args.base64:
1733        runners.append(Base64Runner(args))
1734
1735    if args.bytecode:
1736        add_directory_for_bytecode(runners, args)
1737
1738    if args.aop_transform:
1739        add_cmd_for_aop_transform(runners, args)
1740
1741    if args.debug:
1742        add_directory_for_debug(runners, args)
1743
1744    failed_tests = 0
1745
1746    for runner in runners:
1747        runner.run()
1748        failed_tests += runner.summarize()
1749
1750    # TODO: exit 1 when we have failed tests after all tests are fixed
1751    exit(0)
1752
1753
1754if __name__ == "__main__":
1755    main()
1756