13af6ab5fSopenharmony_ci#!/usr/bin/env python3
23af6ab5fSopenharmony_ci# -*- coding: utf-8 -*-
33af6ab5fSopenharmony_ci#
43af6ab5fSopenharmony_ci# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
53af6ab5fSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
63af6ab5fSopenharmony_ci# you may not use this file except in compliance with the License.
73af6ab5fSopenharmony_ci# You may obtain a copy of the License at
83af6ab5fSopenharmony_ci#
93af6ab5fSopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0
103af6ab5fSopenharmony_ci#
113af6ab5fSopenharmony_ci# Unless required by applicable law or agreed to in writing, software
123af6ab5fSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
133af6ab5fSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
143af6ab5fSopenharmony_ci# See the License for the specific language governing permissions and
153af6ab5fSopenharmony_ci# limitations under the License.
163af6ab5fSopenharmony_ci
173af6ab5fSopenharmony_cifrom glob import glob
183af6ab5fSopenharmony_cifrom os import path
193af6ab5fSopenharmony_ciimport os
203af6ab5fSopenharmony_ciimport re
213af6ab5fSopenharmony_ciimport shutil
223af6ab5fSopenharmony_ciimport subprocess
233af6ab5fSopenharmony_ci
243af6ab5fSopenharmony_ci
253af6ab5fSopenharmony_ciclass Test262Util:
263af6ab5fSopenharmony_ci    def __init__(self):
273af6ab5fSopenharmony_ci        self.header = re.compile(
283af6ab5fSopenharmony_ci            r"\/\*---(?P<header>.+)---\*\/", re.DOTALL)
293af6ab5fSopenharmony_ci        self.includes = re.compile(r"includes:\s+\[(?P<includes>.+)\]")
303af6ab5fSopenharmony_ci        self.includes2 = re.compile(r"includes:(?P<includes>(\s+-[^-].+)+)")
313af6ab5fSopenharmony_ci        self.flags = re.compile(r"flags:\s+\[(?P<flags>.+)\]")
323af6ab5fSopenharmony_ci        self.negative = re.compile(
333af6ab5fSopenharmony_ci            r"negative:.*phase:\s+(?P<phase>\w+).*type:\s+(?P<type>\w+)",
343af6ab5fSopenharmony_ci            re.DOTALL)
353af6ab5fSopenharmony_ci        self.async_ok = re.compile(r"Test262:AsyncTestComplete")
363af6ab5fSopenharmony_ci
373af6ab5fSopenharmony_ci    def generate(self, revision, build_dir, harness_path, show_progress):
383af6ab5fSopenharmony_ci        dest_path = path.join(build_dir, 'test262')
393af6ab5fSopenharmony_ci        stamp_file = path.join(dest_path, 'test262.stamp')
403af6ab5fSopenharmony_ci
413af6ab5fSopenharmony_ci        if path.isfile(stamp_file):
423af6ab5fSopenharmony_ci            return dest_path
433af6ab5fSopenharmony_ci
443af6ab5fSopenharmony_ci        test262_path = path.join(path.sep, 'tmp', 'test262-%s' % revision)
453af6ab5fSopenharmony_ci
463af6ab5fSopenharmony_ci        if not path.exists(test262_path):
473af6ab5fSopenharmony_ci            archive_file = path.join(path.sep, 'tmp', 'test262.zip')
483af6ab5fSopenharmony_ci
493af6ab5fSopenharmony_ci            print("Downloading test262")
503af6ab5fSopenharmony_ci
513af6ab5fSopenharmony_ci            cmd = ['wget', '-q', '-O', archive_file,
523af6ab5fSopenharmony_ci                   'https://github.com/tc39/test262/archive/%s.zip' % revision]
533af6ab5fSopenharmony_ci
543af6ab5fSopenharmony_ci            if show_progress:
553af6ab5fSopenharmony_ci                cmd.append('--show-progress')
563af6ab5fSopenharmony_ci
573af6ab5fSopenharmony_ci            return_code = subprocess.call(cmd)
583af6ab5fSopenharmony_ci
593af6ab5fSopenharmony_ci            if return_code:
603af6ab5fSopenharmony_ci                raise Exception('Downloading test262 repository failed.')
613af6ab5fSopenharmony_ci
623af6ab5fSopenharmony_ci            print("Extracting archive")
633af6ab5fSopenharmony_ci            if path.isdir(test262_path):
643af6ab5fSopenharmony_ci                shutil.rmtree(test262_path)
653af6ab5fSopenharmony_ci
663af6ab5fSopenharmony_ci            return_code = subprocess.call(
673af6ab5fSopenharmony_ci                ['unzip', '-q', '-d', path.join(path.sep, 'tmp'), archive_file])
683af6ab5fSopenharmony_ci
693af6ab5fSopenharmony_ci            if return_code:
703af6ab5fSopenharmony_ci                raise Exception(
713af6ab5fSopenharmony_ci                    'Failed to unzip test262 repository')
723af6ab5fSopenharmony_ci
733af6ab5fSopenharmony_ci            os.remove(archive_file)
743af6ab5fSopenharmony_ci
753af6ab5fSopenharmony_ci        print("Generating tests")
763af6ab5fSopenharmony_ci        src_path = path.join(test262_path, 'test')
773af6ab5fSopenharmony_ci
783af6ab5fSopenharmony_ci        glob_expression = path.join(src_path, "**/*.js")
793af6ab5fSopenharmony_ci        files = glob(glob_expression, recursive=True)
803af6ab5fSopenharmony_ci        files = list(filter(lambda f: not f.endswith("FIXTURE.js"), files))
813af6ab5fSopenharmony_ci
823af6ab5fSopenharmony_ci        with open(harness_path, 'r') as fp:
833af6ab5fSopenharmony_ci            harness = fp.read()
843af6ab5fSopenharmony_ci
853af6ab5fSopenharmony_ci        harness = harness.replace('$SOURCE', '`%s`' % harness)
863af6ab5fSopenharmony_ci
873af6ab5fSopenharmony_ci        for src_file in files:
883af6ab5fSopenharmony_ci            dest_file = src_file.replace(src_path, dest_path)
893af6ab5fSopenharmony_ci            os.makedirs(path.dirname(dest_file), exist_ok=True)
903af6ab5fSopenharmony_ci            self.create_file(src_file, dest_file, harness, test262_path)
913af6ab5fSopenharmony_ci
923af6ab5fSopenharmony_ci        with open(stamp_file, 'w') as fp:
933af6ab5fSopenharmony_ci            pass
943af6ab5fSopenharmony_ci
953af6ab5fSopenharmony_ci        return dest_path
963af6ab5fSopenharmony_ci
973af6ab5fSopenharmony_ci    def create_file(self, input_file, output_file, harness, test262_dir):
983af6ab5fSopenharmony_ci        with open(input_file, 'r') as fp:
993af6ab5fSopenharmony_ci            input_str = fp.read()
1003af6ab5fSopenharmony_ci
1013af6ab5fSopenharmony_ci        header = self.get_header(input_str)
1023af6ab5fSopenharmony_ci        desc = self.parse_descriptor(header)
1033af6ab5fSopenharmony_ci
1043af6ab5fSopenharmony_ci        out_str = header
1053af6ab5fSopenharmony_ci        out_str += "\n"
1063af6ab5fSopenharmony_ci        out_str += harness
1073af6ab5fSopenharmony_ci
1083af6ab5fSopenharmony_ci        for include in desc['includes']:
1093af6ab5fSopenharmony_ci            out_str += "//------------ %s start ------------\n" % include
1103af6ab5fSopenharmony_ci            with open(path.join(test262_dir, 'harness', include), 'r') as fp:
1113af6ab5fSopenharmony_ci                harness_str = fp.read()
1123af6ab5fSopenharmony_ci            out_str += harness_str
1133af6ab5fSopenharmony_ci            out_str += "//------------ %s end ------------\n" % include
1143af6ab5fSopenharmony_ci            out_str += "\n"
1153af6ab5fSopenharmony_ci
1163af6ab5fSopenharmony_ci        out_str += input_str
1173af6ab5fSopenharmony_ci        with open(output_file, 'w') as o:
1183af6ab5fSopenharmony_ci            o.write(out_str)
1193af6ab5fSopenharmony_ci
1203af6ab5fSopenharmony_ci    def get_header(self, content):
1213af6ab5fSopenharmony_ci        header_comment = self.header.search(content)
1223af6ab5fSopenharmony_ci        assert header_comment
1233af6ab5fSopenharmony_ci        return header_comment.group(0)
1243af6ab5fSopenharmony_ci
1253af6ab5fSopenharmony_ci    def parse_descriptor(self, header):
1263af6ab5fSopenharmony_ci        match = self.includes.search(header)
1273af6ab5fSopenharmony_ci        includes = list(map(lambda e: e.strip(), match.group(
1283af6ab5fSopenharmony_ci            'includes').split(','))) if match else []
1293af6ab5fSopenharmony_ci
1303af6ab5fSopenharmony_ci        match = self.includes2.search(header)
1313af6ab5fSopenharmony_ci        includes += list(map(lambda e: e.strip(), match.group(
1323af6ab5fSopenharmony_ci            'includes').split('-')))[1:] if match else []
1333af6ab5fSopenharmony_ci
1343af6ab5fSopenharmony_ci        includes.extend(['assert.js', 'sta.js'])
1353af6ab5fSopenharmony_ci
1363af6ab5fSopenharmony_ci        match = self.flags.search(header)
1373af6ab5fSopenharmony_ci        flags = list(map(lambda e: e.strip(),
1383af6ab5fSopenharmony_ci                         match.group('flags').split(','))) if match else []
1393af6ab5fSopenharmony_ci
1403af6ab5fSopenharmony_ci        if 'async' in flags:
1413af6ab5fSopenharmony_ci            includes.extend(['doneprintHandle.js'])
1423af6ab5fSopenharmony_ci
1433af6ab5fSopenharmony_ci        match = self.negative.search(header)
1443af6ab5fSopenharmony_ci        negative_phase = match.group('phase') if match else 'pass'
1453af6ab5fSopenharmony_ci        negative_type = match.group('type') if match else ''
1463af6ab5fSopenharmony_ci
1473af6ab5fSopenharmony_ci        # negative_phase: pass, parse, resolution, runtime
1483af6ab5fSopenharmony_ci        return {
1493af6ab5fSopenharmony_ci            'flags': flags,
1503af6ab5fSopenharmony_ci            'negative_phase': negative_phase,
1513af6ab5fSopenharmony_ci            'negative_type': negative_type,
1523af6ab5fSopenharmony_ci            'includes': includes,
1533af6ab5fSopenharmony_ci        }
1543af6ab5fSopenharmony_ci
1553af6ab5fSopenharmony_ci    @staticmethod
1563af6ab5fSopenharmony_ci    def validate_parse_result(return_code, std_err, desc, out):
1573af6ab5fSopenharmony_ci        is_negative = (desc['negative_phase'] == 'parse')
1583af6ab5fSopenharmony_ci
1593af6ab5fSopenharmony_ci        if return_code == 0:  # passed
1603af6ab5fSopenharmony_ci            if is_negative:
1613af6ab5fSopenharmony_ci                return False, False  # negative test passed
1623af6ab5fSopenharmony_ci
1633af6ab5fSopenharmony_ci            return True, True  # positive test passed
1643af6ab5fSopenharmony_ci
1653af6ab5fSopenharmony_ci        if return_code == 1:  # failed
1663af6ab5fSopenharmony_ci            return is_negative and (desc['negative_type'] in out), False
1673af6ab5fSopenharmony_ci
1683af6ab5fSopenharmony_ci        return False, False  # abnormal
1693af6ab5fSopenharmony_ci
1703af6ab5fSopenharmony_ci    def validate_runtime_result(self, return_code, std_err, desc, out):
1713af6ab5fSopenharmony_ci        is_negative = (desc['negative_phase'] == 'runtime') or (
1723af6ab5fSopenharmony_ci            desc['negative_phase'] == 'resolution')
1733af6ab5fSopenharmony_ci
1743af6ab5fSopenharmony_ci        if return_code == 0:  # passed
1753af6ab5fSopenharmony_ci            if is_negative:
1763af6ab5fSopenharmony_ci                return False  # negative test passed
1773af6ab5fSopenharmony_ci
1783af6ab5fSopenharmony_ci            passed = (len(std_err) == 0)
1793af6ab5fSopenharmony_ci            if 'async' in desc['flags']:
1803af6ab5fSopenharmony_ci                passed = passed and bool(self.async_ok.match(out))
1813af6ab5fSopenharmony_ci            return passed  # positive test passed?
1823af6ab5fSopenharmony_ci
1833af6ab5fSopenharmony_ci        if return_code == 1:  # failed
1843af6ab5fSopenharmony_ci            return is_negative and (desc['negative_type'] in std_err)
1853af6ab5fSopenharmony_ci
1863af6ab5fSopenharmony_ci        return False  # abnormal
187