• 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
19import os
20import re
21import shutil
22import subprocess
23
24
25class Test262Util:
26    def __init__(self):
27        self.header = re.compile(
28            r"\/\*---(?P<header>.+)---\*\/", re.DOTALL)
29        self.includes = re.compile(r"includes:\s+\[(?P<includes>.+)\]")
30        self.includes2 = re.compile(r"includes:(?P<includes>(\s+-[^-].+)+)")
31        self.flags = re.compile(r"flags:\s+\[(?P<flags>.+)\]")
32        self.negative = re.compile(
33            r"negative:.*phase:\s+(?P<phase>\w+).*type:\s+(?P<type>\w+)",
34            re.DOTALL)
35        self.async_ok = re.compile(r"Test262:AsyncTestComplete")
36
37    def generate(self, revision, build_dir, harness_path, show_progress):
38        dest_path = path.join(build_dir, 'test262')
39        stamp_file = path.join(dest_path, 'test262.stamp')
40
41        if path.isfile(stamp_file):
42            return dest_path
43
44        test262_path = path.join(path.sep, 'tmp', 'test262-%s' % revision)
45
46        if not path.exists(test262_path):
47            archive_file = path.join(path.sep, 'tmp', 'test262.zip')
48
49            print("Downloading test262")
50
51            cmd = ['wget', '-q', '-O', archive_file,
52                   'https://github.com/tc39/test262/archive/%s.zip' % revision]
53
54            if show_progress:
55                cmd.append('--show-progress')
56
57            return_code = subprocess.call(cmd)
58
59            if return_code:
60                raise Exception('Downloading test262 repository failed.')
61
62            print("Extracting archive")
63            if path.isdir(test262_path):
64                shutil.rmtree(test262_path)
65
66            return_code = subprocess.call(
67                ['unzip', '-q', '-d', path.join(path.sep, 'tmp'), archive_file])
68
69            if return_code:
70                raise Exception(
71                    'Failed to unzip test262 repository')
72
73            os.remove(archive_file)
74
75        print("Generating tests")
76        src_path = path.join(test262_path, 'test')
77
78        glob_expression = path.join(src_path, "**/*.js")
79        files = glob(glob_expression, recursive=True)
80        files = list(filter(lambda f: not f.endswith("FIXTURE.js"), files))
81
82        with open(harness_path, 'r') as fp:
83            harness = fp.read()
84
85        harness = harness.replace('$SOURCE', '`%s`' % harness)
86
87        for src_file in files:
88            dest_file = src_file.replace(src_path, dest_path)
89            os.makedirs(path.dirname(dest_file), exist_ok=True)
90            self.create_file(src_file, dest_file, harness, test262_path)
91
92        with open(stamp_file, 'w') as fp:
93            pass
94
95        return dest_path
96
97    def create_file(self, input_file, output_file, harness, test262_dir):
98        with open(input_file, 'r') as fp:
99            input_str = fp.read()
100
101        header = self.get_header(input_str)
102        desc = self.parse_descriptor(header)
103
104        out_str = header
105        out_str += "\n"
106        out_str += harness
107
108        for include in desc['includes']:
109            out_str += "//------------ %s start ------------\n" % include
110            with open(path.join(test262_dir, 'harness', include), 'r') as fp:
111                harness_str = fp.read()
112            out_str += harness_str
113            out_str += "//------------ %s end ------------\n" % include
114            out_str += "\n"
115
116        out_str += input_str
117        with open(output_file, 'w') as o:
118            o.write(out_str)
119
120    def get_header(self, content):
121        header_comment = self.header.search(content)
122        assert header_comment
123        return header_comment.group(0)
124
125    def parse_descriptor(self, header):
126        match = self.includes.search(header)
127        includes = list(map(lambda e: e.strip(), match.group(
128            'includes').split(','))) if match else []
129
130        match = self.includes2.search(header)
131        includes += list(map(lambda e: e.strip(), match.group(
132            'includes').split('-')))[1:] if match else []
133
134        includes.extend(['assert.js', 'sta.js'])
135
136        match = self.flags.search(header)
137        flags = list(map(lambda e: e.strip(),
138                         match.group('flags').split(','))) if match else []
139
140        if 'async' in flags:
141            includes.extend(['doneprintHandle.js'])
142
143        match = self.negative.search(header)
144        negative_phase = match.group('phase') if match else 'pass'
145        negative_type = match.group('type') if match else ''
146
147        # negative_phase: pass, parse, resolution, runtime
148        return {
149            'flags': flags,
150            'negative_phase': negative_phase,
151            'negative_type': negative_type,
152            'includes': includes,
153        }
154
155    @staticmethod
156    def validate_parse_result(return_code, std_err, desc, out):
157        is_negative = (desc['negative_phase'] == 'parse')
158
159        if return_code == 0:  # passed
160            if is_negative:
161                return False, False  # negative test passed
162
163            return True, True  # positive test passed
164
165        if return_code == 1:  # failed
166            return is_negative and (desc['negative_type'] in out), False
167
168        return False, False  # abnormal
169
170    def validate_runtime_result(self, return_code, std_err, desc, out):
171        is_negative = (desc['negative_phase'] == 'runtime') or (
172            desc['negative_phase'] == 'resolution')
173
174        if return_code == 0:  # passed
175            if is_negative:
176                return False  # negative test passed
177
178            passed = (len(std_err) == 0)
179            if 'async' in desc['flags']:
180                passed = passed and bool(self.async_ok.match(out))
181            return passed  # positive test passed?
182
183        if return_code == 1:  # failed
184            return is_negative and (desc['negative_type'] in std_err)
185
186        return False  # abnormal
187