1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (c) 2023-2024 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16 17import os 18import re 19import yaml 20import platform 21import subprocess 22from tool.test_helper import read_declaration 23 24STRICT_OFF = ['--strict', 'false'] 25STRICT_ON = ['--strict', 'true'] 26MODULE = ['--module'] 27DECORATOR = ['--experimentalDecorators'] 28STRICTNULLCHECKS = ['--strictNullChecks'] 29JSX = ['--jsx'] 30 31 32def get_error_message(strs, filename): 33 if platform.system().lower() == 'windows': 34 filename = filename.replace('\\', '/') 35 if len(re.findall(filename + r':(\d+)', strs)) > 0: 36 line_number = re.findall(filename + r':(\d+)', strs) 37 else: 38 line_number = 0 39 err_message = strs 40 return err_message, line_number 41 42 43class TestCase: 44 temp_path = "" 45 ld_library_path = "" 46 js_runtime_path = "" 47 es2abc = "" 48 tsc = "" 49 enable_arkguard = False 50 51 def __init__(self, path): 52 self.path = path 53 self.target_js_path = "" 54 try: 55 data = yaml.safe_load(read_declaration(path)) 56 except: 57 data = {} 58 self.declaration = data 59 self.fail = False 60 self.is_test_case = False if data is None else True 61 self.detail_result = "" 62 self.err_line = 0 63 self.abc_file_path = "" 64 self.abc_file_path_temp = "" 65 66 def check_declaration(self): 67 if self.declaration == {}: 68 self.detail_result = "parse test case declaration failed, maybe bad format." 69 return False 70 if 'error' in self.declaration: 71 if self.declaration['error'] is None or 'code' not in self.declaration['error'] and 'type' not in \ 72 self.declaration['error']: 73 self.detail_result = "neither error code nor error type are defined in negative case." 74 return False 75 return True 76 77 # create abc files 78 def create_abc(self, filename): 79 process = subprocess.Popen(self.__get_es2abc_cmd(filename), stdin=subprocess.PIPE, stdout=subprocess.PIPE, 80 stderr=subprocess.PIPE) 81 out, err = process.communicate(timeout=5000) 82 return_code = process.returncode 83 if return_code != 0: 84 err_msg, line = get_error_message( 85 out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore"), filename) 86 self.detail_result = err_msg 87 self.err_line = line 88 self.fail = True 89 return 90 if "TESTCASE SUCCESS" not in out.decode("utf-8", errors="ignore"): 91 self.detail_result = "check stdout failed!" 92 self.fail = True 93 return 94 95 def execute(self, ark_runtime=False): 96 if not self.is_test_case: 97 return 98 if ark_runtime: 99 with open(self.path, 'a') as file_added: 100 if '.d.ts' not in self.path: 101 file_added.write('\rprint("TESTCASE SUCCESS");') 102 self.__test_es2abc() 103 if os.path.exists(self.abc_file_path): 104 os.remove(self.abc_file_path) 105 else: 106 self.__tsc_test() 107 108 def execute_arkguard(self, input_file_path): 109 arkguard_root_dir = os.path.join("../../../../", "arkcompiler/ets_frontend/arkguard") 110 arkgurad_entry_path = os.path.join(arkguard_root_dir, "lib/cli/SecHarmony.js") 111 config_path = os.path.join(arkguard_root_dir, "test/compilerTestConfig.json") 112 arkguard_cmd = [ 113 'node', 114 '--no-warnings', 115 arkgurad_entry_path, 116 input_file_path, 117 '--config-path', 118 config_path, 119 '--inplace' 120 ] 121 122 process = subprocess.Popen(arkguard_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 123 out, err = process.communicate() 124 if err: 125 print("arkguard error: ", err) 126 process.wait() 127 128 def experimental_decorators(self): 129 if 'experimentalDecorators' in self.declaration: 130 return True 131 return False 132 133 def is_negative(self): 134 if 'error' in self.declaration: 135 return True 136 return False 137 138 def is_current(self): 139 if 'isCurrent' in self.declaration: 140 return True 141 return False 142 143 def null_checks(self): 144 if 'strictNullChecks' in self.declaration: 145 return True 146 return False 147 148 def is_set_module(self): 149 if 'module' in self.declaration: 150 return True 151 return False 152 153 def is_jsx(self): 154 if 'jsx' in self.declaration: 155 return True 156 return False 157 158 def __error_code(self): 159 if 'code' in self.declaration['error']: 160 return self.declaration['error']['code'] 161 return None 162 163 def __error_type(self): 164 if 'type' in self.declaration['error']: 165 return self.declaration['error']['type'] 166 return None 167 168 def __get_tsc_cmd(self): 169 if platform.system().lower() == 'windows': 170 cmd = ['cmd', '/c', 'tsc', '--target', 'es2021', '--moduleResolution', 'node'] 171 else: 172 cmd = [TestCase.tsc, '--target', 'es2021', '--moduleResolution', 'node'] 173 if self.__is_strict(): 174 cmd.extend(STRICT_ON) 175 else: 176 cmd.extend(STRICT_OFF) 177 if self.is_set_module(): 178 cmd.extend(MODULE) 179 cmd.append('es2020') 180 if self.experimental_decorators(): 181 cmd.extend(DECORATOR) 182 cmd.append('true') 183 if self.null_checks(): 184 cmd.extend(STRICTNULLCHECKS) 185 cmd.append('false') 186 if self.is_jsx(): 187 cmd.extend(JSX) 188 cmd.append('react-jsx') 189 if self.is_current(): 190 cmd.append(self.path) 191 cmd.append('--outDir') 192 cmd.append(TestCase.temp_path) 193 self.target_js_path = TestCase.temp_path + self.__get_js_basename() 194 return cmd 195 196 def __get_node_cmd(self): 197 cmd = ['node'] 198 if self.is_current(): 199 cmd.append(self.target_js_path) 200 else: 201 cmd.append(TestCase.temp_path + self.__get_js_basename()) 202 return cmd 203 204 # get es2abc --merge-abc 205 def __get_es2abc_cmd(self, file_path): 206 abc_file_path = ("%s.abc" % (os.path.splitext(file_path)[0])) 207 self.abc_file_path_temp = abc_file_path 208 cmd = [TestCase.es2abc + 'es2abc'] 209 if 'RequireCommandrecordsource' in self.path: 210 cmd.extend(['--module', '--record-source', '--output', abc_file_path, file_path]) 211 else: 212 cmd.extend(['--module', '--output', abc_file_path, file_path]) 213 return cmd 214 215 # get es2abc file commands 216 def _get_ark_js_cmd(self): 217 os.environ.setdefault("LD_LIBRARY_PATH", TestCase.ld_library_path) 218 run_abc_cmd = [os.path.join(TestCase.js_runtime_path, 'ark_js_vm'), self.abc_file_path_temp] 219 return run_abc_cmd 220 pass 221 222 def __get_js_basename(self): 223 sp = '/' 224 if platform.system().lower() == 'windows': 225 sp = '\\' 226 return "test_ts_cases" + sp + self.path.split(sp + "test_ts_cases" + sp)[1].replace('.tsx', '.js').replace('.ts', '.js') 227 228 def __is_strict(self): 229 if 'strict' in self.declaration: 230 return bool(self.declaration['strict']) 231 return True 232 233 def __tsc_test(self): 234 process = subprocess.Popen(self.__get_tsc_cmd(), stdin=subprocess.PIPE, stdout=subprocess.PIPE, 235 stderr=subprocess.PIPE) 236 out, err = process.communicate(timeout=5000) 237 return_code = process.returncode 238 if self.is_negative(): 239 if return_code == 0: 240 self.fail = True 241 self.detail_result = "No error found in negative case." 242 return 243 if self.__error_code() in out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore"): 244 return 245 self.fail = True 246 self.detail_result = "Error code not as expected." 247 return 248 # positive case 249 if return_code != 0: 250 self.detail_result = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 251 self.fail = True 252 return 253 if self.is_current(): 254 with open(self.target_js_path, 'a') as fileAdded: 255 fileAdded.write('console.log("TESTCASE SUCCESS");') 256 # run node command 257 process = subprocess.Popen(self.__get_node_cmd(), stdin=subprocess.PIPE, stdout=subprocess.PIPE, 258 stderr=subprocess.PIPE) 259 out, err = process.communicate(timeout=5000) 260 return_code = process.returncode 261 if self.is_current(): 262 if os.path.exists(self.target_js_path): 263 os.remove(self.target_js_path) 264 if return_code != 0: 265 err_msg, line = get_error_message( 266 out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore"), self.__get_js_basename()) 267 self.detail_result = err_msg 268 self.err_line = line 269 self.fail = True 270 return 271 # check std out 272 if "TESTCASE SUCCESS" not in out.decode("utf-8", errors="ignore"): 273 self.detail_result = "check stdout failed!" 274 self.fail = True 275 return 276 277 278 def __test_es2abc(self): 279 # run arkguard 280 if TestCase.enable_arkguard: 281 self.execute_arkguard(self.path) 282 283 # compiler to abc 284 process = subprocess.Popen(self.__get_es2abc_cmd(self.path), stdin=subprocess.PIPE, stdout=subprocess.PIPE, 285 stderr=subprocess.PIPE) 286 out, err = process.communicate(timeout=5000) 287 return_code = process.returncode 288 if self.is_negative(): 289 if return_code == 0: 290 self.fail = True 291 return 292 if self.__error_type() in out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore"): 293 return 294 self.fail = True 295 self.detail_result = "Error type not as expected." 296 return 297 # positive case 298 if return_code != 0: 299 self.detail_result = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 300 self.fail = True 301 return 302 # execute ark_js_vm 303 process = subprocess.Popen(self._get_ark_js_cmd(), stdin=subprocess.PIPE, stdout=subprocess.PIPE, 304 stderr=subprocess.PIPE) 305 out, err = process.communicate(timeout=5000) 306 return_code = process.returncode 307 if return_code != 0: 308 err_msg, line = get_error_message( 309 out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore"), 310 os.path.basename(self.abc_file_path)) 311 self.detail_result = err_msg 312 self.err_line = line 313 self.fail = True 314 return 315 # check std out 316 if "TESTCASE SUCCESS" not in out.decode("utf-8", errors="ignore") and '.d.ts' not in self.path: 317 self.detail_result = "check stdout failed!" 318 self.fail = True 319 return