• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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