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