1#!/usr/bin/env python3 2#coding: utf-8 3 4""" 5Copyright (c) 2021-2022 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17 18Description: run script 19 expect_output will get run result, 20 expect_sub_output will catch pivotal sub output, 21 expect_file will get print string 22""" 23 24import argparse 25import os 26import subprocess 27import sys 28import time 29import zipfile 30from io import StringIO 31 32 33def get_env_path_from_rsp(script_file: str) -> list: 34 """get env path from response file recursively.""" 35 rsp_file = "{0}{1}".format(script_file, ".rsp") 36 if not os.path.exists(rsp_file): 37 print( 38 "File \"{}\" does not exist!\n" \ 39 "This indicates that its related shared_library is not compiled by this project, but there is an " \ 40 "executable or shared_library depend on its related shared_library!".format(rsp_file)) 41 sys.exit(1) 42 43 rsp_info_list = [] 44 with open(rsp_file, "r") as fi: 45 rsp_info_str = fi.read() 46 rsp_info_list = rsp_info_str.split(" ") 47 48 env_path_list = [] 49 for element in rsp_info_list: 50 if element.endswith(".so") or element.endswith(".dll"): 51 env_path_list.extend(get_env_path_from_rsp(element)) 52 env_path_list.append(os.path.dirname(element)) 53 return env_path_list 54 55 56def get_command_and_env_path(args: object) -> [str, str]: 57 """get command and environment path from args for running excutable.""" 58 if args.arkjsvmpath is None: 59 env_path_list = list(set(get_env_path_from_rsp(args.script_file))) 60 env_path_list.append(args.clang_lib_path) 61 env_path = ":".join(env_path_list) 62 else: 63 env_path_list = list() 64 env_path = "" 65 if args.qemu_binary_path: 66 if not os.path.exists(args.qemu_binary_path): 67 print("Have you set up environment for running executables with qemu?\n" \ 68 "If not, get set-up steps from https://gitee.com/ark_standalone_build/docs ," \ 69 " append your build command of ark.py with option \"--clean-continue\"," \ 70 " and execute the appended command after setting up the environment.\n" \ 71 "If yes, the environment settings for qemu on your host machine may be different from what the link" \ 72 " above shows, it is suggested to match your local environment settings with what the link shows.") 73 sys.exit(1) 74 cmd = \ 75 "{}".format(args.qemu_binary_path) + \ 76 " -L {}".format(args.qemu_ld_prefix) + \ 77 " -E LD_LIBRARY_PATH={}".format(env_path) + \ 78 " {}".format(args.script_file) 79 else: 80 cmd = "{}".format(args.script_file) 81 cmd += " {}".format(args.script_options) if args.script_options else "" 82 cmd += " {}".format(args.script_args) if args.script_args else "" 83 return [cmd, env_path] 84 85 86def parse_args() -> object: 87 """parse arguments.""" 88 parser = argparse.ArgumentParser() 89 parser.add_argument('--script-file', help='execute script file') 90 parser.add_argument('--script-options', help='execute script options') 91 parser.add_argument('--script-args', help='args of script') 92 parser.add_argument('--expect-output', help='expect output') 93 parser.add_argument('--expect-sub-output', help='expect sub output') 94 parser.add_argument('--expect-file', help='expect file') 95 parser.add_argument('--env-path', help='LD_LIBRARY_PATH env') 96 parser.add_argument('--timeout-limit', help='timeout limit') 97 parser.add_argument('--clang-lib-path', help='part for LD_LIBRARY_PATH, it is not in .rsp file') 98 parser.add_argument('--qemu-binary-path', help='path to qemu binary, run executable with qemu if assigned') 99 parser.add_argument('--qemu-ld-prefix', help='elf interpreter prefix') 100 parser.add_argument('--expect-sub-error', help='use error output to compare result') 101 parser.add_argument('--L', help='qemu lib path') 102 parser.add_argument('--arkjsvmpath', help='ark_js_vm_path') 103 parser.add_argument('--test-abc-path', help='abc path') 104 parser.add_argument('--skip', type=bool, default=False, help='skip execute') 105 args = parser.parse_args() 106 return args 107 108 109def process_open(args: object) -> [str, object]: 110 """get command and open subprocess.""" 111 if args.env_path: 112 # use the given env-path 113 cmd = args.script_file 114 cmd += " {}".format(args.script_options) if args.script_options else "" 115 cmd += " {}".format(args.script_args) if args.script_args else "" 116 # process for running executable directly 117 subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 118 env={'LD_LIBRARY_PATH': str(args.env_path)}) 119 else: 120 # get env-path from response file recursively 121 [cmd, env_path] = get_command_and_env_path(args) 122 args.env_path = env_path 123 if args.qemu_binary_path: 124 # process for running executable with qemu 125 subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 126 else: 127 # process for running executable directly 128 subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 129 env={'LD_LIBRARY_PATH': str(env_path)}) 130 return [cmd, subp] 131 132 133def generate_stub_code_comment(out_str:str): 134 dir_path = './gen/arkcompiler/ets_runtime/' 135 if not os.path.exists(dir_path): 136 return 137 zip_path = dir_path + 'stub_code_comment.zip' 138 if out_str != '': 139 memory_file = StringIO() 140 memory_file.write(out_str) 141 memory_file.seek(0) 142 with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file: 143 zip_file.writestr('stub_code_comment.txt', memory_file.getvalue()) 144 145 146def check_expect_output(cmd: str, returncode: str, out_str: str, err_str: str, expect_output: str, start_time: float, env_path: str): 147 """Check if return code matches expected output.""" 148 if returncode != expect_output: 149 print(">>>>> ret <<<<<") 150 print(returncode) 151 print(">>>>> out <<<<<") 152 print(out_str) 153 print(">>>>> err <<<<<") 154 print(err_str) 155 print(">>>>> Expect return: [" + expect_output \ 156 + "]\n>>>>> But got: [" + returncode + "]") 157 raise RuntimeError("Run [LD_LIBRARY_PATH=" + env_path + " " + cmd + "] failed! used: %.5f seconds" % (time.time() - start_time)) 158 159 160def check_expect_sub_output(cmd: str, returncode: str, out_str: str, err_str: str, expect_sub_output: str, start_time: float, env_path: str): 161 """Check if output contains expected substring.""" 162 if out_str.find(expect_sub_output) == -1 or returncode != "0": 163 print(">>>>> ret <<<<<") 164 print(returncode) 165 print(">>>>> err <<<<<") 166 print(err_str) 167 print(">>>>> Expect contain: [" + expect_sub_output \ 168 + "]\n>>>>> But got: [" + out_str + "]") 169 raise RuntimeError("Run [LD_LIBRARY_PATH=" + env_path + " " + cmd + "] failed! used: %.5f seconds" % (time.time() - start_time)) 170 171 172def check_expect_sub_error(cmd: str, returncode: str, out_str: str, err_str: str, expect_sub_error: str, start_time: float, env_path: str): 173 """Check if error output contains expected substring.""" 174 if err_str.find(expect_sub_error) == -1 or returncode == '0': 175 print(">>>>> returnCode <<<<<") 176 print(returncode) 177 print(">>>>> expect err <<<<<") 178 print(err_str) 179 if returncode == '0': 180 print(">>>>> Expect err : [" + expect_sub_error \ 181 + "]\n>>>>> But got corret: [" + out_str + "]") 182 else: 183 print(">>>>> Expect err : [" + expect_sub_error \ 184 + "]\n>>>>> But got wrong err: [" + err_str + "]") 185 raise RuntimeError("Run [LD_LIBRARY_PATH=" + env_path + " " + cmd + "] failed! used: %.5f seconds" % (time.time() - start_time)) 186 187 188def check_expect_file(cmd: str, returncode: str, out_str: str, err_str: str, expect_file: str, start_time: float, env_path: str): 189 """Check if output matches expected file content.""" 190 with open(expect_file, mode='r') as file: 191 # skip license header 192 expect_output = ''.join(file.readlines()[13:]) 193 file.close() 194 result_cmp = compare_line_by_line(expect_output, out_str) 195 if result_cmp or returncode != "0": 196 print(">>>>> ret <<<<<") 197 print(returncode) 198 print(">>>>> err <<<<<") 199 print(err_str) 200 print(">>>>> Expect {} lines: [{}]\n>>>>> But got {} lines: [{}]".format( 201 expect_output.count('\n'), expect_output, out_str.count('\n'), out_str 202 )) 203 raise RuntimeError("Run [LD_LIBRARY_PATH=" + env_path + " " + cmd + "] failed! used: %.5f seconds" % (time.time() - start_time)) 204 205 206def judge_output(args: object): 207 """Run executable and judge if success or not.""" 208 209 if args.skip: 210 return 211 212 start_time = time.time() 213 [cmd, subp] = process_open(args) 214 timeout_limit = int(args.timeout_limit) if args.timeout_limit else 2400 # units: s 215 216 try: 217 out, err = subp.communicate(timeout=timeout_limit) 218 except subprocess.TimeoutExpired: 219 raise RuntimeError('Run [LD_LIBRARY_PATH=' + args.env_path + " " + cmd + '] timeout, timeout_limit = ', timeout_limit, 's') 220 221 out_str = out.decode('UTF-8', errors="ignore") 222 err_str = err.decode('UTF-8', errors="ignore") 223 generate_stub_code_comment(out_str) 224 returncode = str(subp.returncode) 225 226 if args.expect_output: 227 check_expect_output(cmd, returncode, out_str, err_str, args.expect_output, start_time, args.env_path) 228 elif args.expect_sub_output: 229 check_expect_sub_output(cmd, returncode, out_str, err_str, args.expect_sub_output, start_time, args.env_path) 230 elif args.expect_sub_error: 231 check_expect_sub_error(cmd, returncode, out_str, err_str, args.expect_sub_error, start_time, args.env_path) 232 elif args.expect_file: 233 check_expect_file(cmd, returncode, out_str, err_str, args.expect_file, start_time, args.env_path) 234 else: 235 raise RuntimeError("Run [LD_LIBRARY_PATH=" + args.env_path + " " + cmd + "] with no expect! used: %.5f seconds" % (time.time() - start_time)) 236 237 print("Run [LD_LIBRARY_PATH=" + args.env_path + " " + cmd + "] success!") 238 print("used: %.5f seconds" % (time.time() - start_time)) 239 240def compare_line_by_line(expect_output:str, got_output:str): 241 expect_output_list = expect_output.split("\n") 242 got_output_list = got_output.split("\n") 243 for index, (expect_line, got_line) in enumerate(zip(expect_output_list, got_output_list)): 244 if expect_line == got_line: 245 continue 246 error_msg = "" 247 248 if "__INT_MORE_PREV__" in expect_line: 249 prev_got_value = reverse_find_first_not_trace_line(got_output_list, index-1) 250 if got_line.isdigit() and prev_got_value.isdigit() and int(prev_got_value) < int(got_line): 251 continue 252 error_msg = "Got integer result is not more than previous integer result" 253 254 if "__INT__" in expect_line: 255 if got_line.isdigit(): 256 continue 257 error_msg = "Got not integer" 258 259 print(">>>>> diff <<<<<") 260 if error_msg: 261 print(error_msg) 262 print("Difference in line {}:\nExcepted: [{}]\nBut got: [{}]".format(index+1, expect_line, got_line)) 263 return True 264 return False 265 266def reverse_find_first_not_trace_line(output_list: list, init_index: int) -> str: 267 for i in range(init_index, -1, -1): 268 if "[trace]" not in output_list[i]: 269 return output_list[i] 270 return "" 271 272if __name__ == '__main__': 273 input_args = parse_args() 274 judge_output(input_args) 275