• 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    env_path_list = list(set(get_env_path_from_rsp(args.script_file)))
59    env_path_list.append(args.clang_lib_path)
60    env_path = ":".join(env_path_list)
61    if args.qemu_binary_path:
62        if not os.path.exists(args.qemu_binary_path):
63            print("Have you set up environment for running executables with qemu?\n" \
64                "If not, get set-up steps from https://gitee.com/ark_standalone_build/docs ," \
65                " append your build command of ark.py with option \"--clean-continue\"," \
66                " and execute the appended command after setting up the environment.\n" \
67                "If yes, the environment settings for qemu on your host machine may be different from what the link" \
68                " above shows, it is suggested to match your local environment settings with what the link shows.")
69            sys.exit(1)
70        cmd = \
71            "{}".format(args.qemu_binary_path) + \
72            " -L {}".format(args.qemu_ld_prefix) + \
73            " -E LD_LIBRARY_PATH={}".format(env_path) + \
74            " {}".format(args.script_file)
75    else:
76        cmd = "{}".format(args.script_file)
77    cmd += " {}".format(args.script_options) if args.script_options else ""
78    cmd += " {}".format(args.script_args) if args.script_args else ""
79    return [cmd, env_path]
80
81
82def parse_args() -> object:
83    """parse arguments."""
84    parser = argparse.ArgumentParser()
85    parser.add_argument('--script-file', help='execute script file')
86    parser.add_argument('--script-options', help='execute script options')
87    parser.add_argument('--script-args', help='args of script')
88    parser.add_argument('--expect-output', help='expect output')
89    parser.add_argument('--expect-sub-output', help='expect sub output')
90    parser.add_argument('--expect-file', help='expect file')
91    parser.add_argument('--env-path', help='LD_LIBRARY_PATH env')
92    parser.add_argument('--timeout-limit', help='timeout limit')
93    parser.add_argument('--clang-lib-path', help='part for LD_LIBRARY_PATH, it is not in .rsp file')
94    parser.add_argument('--qemu-binary-path', help='path to qemu binary, run executable with qemu if assigned')
95    parser.add_argument('--qemu-ld-prefix', help='elf interpreter prefix')
96    parser.add_argument('--expect-sub-error', help='use error output to compare result')
97    args = parser.parse_args()
98    return args
99
100
101def process_open(args: object) -> [str, object]:
102    """get command and open subprocess."""
103    if args.env_path:
104        # use the given env-path
105        cmd = args.script_file
106        cmd += " {}".format(args.script_options) if args.script_options else ""
107        cmd += " {}".format(args.script_args) if args.script_args else ""
108        # process for running executable directly
109        subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
110            env={'LD_LIBRARY_PATH': str(args.env_path)})
111    else:
112        # get env-path from response file recursively
113        [cmd, env_path] = get_command_and_env_path(args)
114        if args.qemu_binary_path:
115            # process for running executable with qemu
116            subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
117        else:
118            # process for running executable directly
119            subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
120                env={'LD_LIBRARY_PATH': str(env_path)})
121    return [cmd, subp]
122
123
124def generate_stub_code_comment(out_str:str):
125    dir_path = './gen/arkcompiler/ets_runtime/'
126    if not os.path.exists(dir_path):
127        return
128    zip_path = dir_path + 'stub_code_comment.zip'
129    if out_str != '':
130        memory_file = StringIO()
131        memory_file.write(out_str)
132        memory_file.seek(0)
133        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file:
134            zip_file.writestr('stub_code_comment.txt', memory_file.getvalue())
135
136
137def check_expect_output(cmd: str, returncode: str, out_str: str, err_str: str, expect_output: str):
138    """Check if return code matches expected output."""
139    if returncode != expect_output:
140        print(">>>>> ret <<<<<")
141        print(returncode)
142        print(">>>>> out <<<<<")
143        print(out_str)
144        print(">>>>> err <<<<<")
145        print(err_str)
146        print(">>>>> Expect return: [" + expect_output \
147            + "]\n>>>>> But got: [" + returncode + "]")
148        raise RuntimeError("Run [" + cmd + "] failed!")
149
150
151def check_expect_sub_output(cmd: str, returncode: str, out_str: str, err_str: str, expect_sub_output: str):
152    """Check if output contains expected substring."""
153    if out_str.find(expect_sub_output) == -1 or returncode != "0":
154        print(">>>>> ret <<<<<")
155        print(returncode)
156        print(">>>>> err <<<<<")
157        print(err_str)
158        print(">>>>> Expect contain: [" + expect_sub_output \
159            + "]\n>>>>> But got: [" + out_str + "]")
160        raise RuntimeError("Run [" + cmd + "] failed!")
161
162
163def check_expect_sub_error(cmd: str, returncode: str, out_str: str, err_str: str, expect_sub_error: str):
164    """Check if error output contains expected substring."""
165    if err_str.find(expect_sub_error) == -1 or returncode == '0':
166        print(">>>>> returnCode <<<<<")
167        print(returncode)
168        print(">>>>> expect err <<<<<")
169        print(err_str)
170        if returncode == '0':
171            print(">>>>> Expect err : [" + expect_sub_error \
172                + "]\n>>>>> But got corret: [" + out_str + "]")
173        else:
174            print(">>>>> Expect err : [" + expect_sub_error \
175                + "]\n>>>>> But got wrong err: [" + err_str + "]")
176        raise RuntimeError("Run [" + cmd + "] failed!")
177
178
179def check_expect_file(cmd: str, returncode: str, out_str: str, err_str: str, expect_file: str):
180    """Check if output matches expected file content."""
181    with open(expect_file, mode='r') as file:
182        # skip license header
183        expect_output = ''.join(file.readlines()[13:])
184        file.close()
185        result_cmp = compare_line_by_line(expect_output, out_str)
186        if result_cmp or returncode != "0":
187            print(">>>>> ret <<<<<")
188            print(returncode)
189            print(">>>>> err <<<<<")
190            print(err_str)
191            print(">>>>> Expect {} lines: [{}]\n>>>>> But got {} lines: [{}]".format(
192                expect_output.count('\n'), expect_output, out_str.count('\n'), out_str
193            ))
194            raise RuntimeError("Run [" + cmd + "] failed!")
195
196
197def judge_output(args: object):
198    """Run executable and judge if success or not."""
199    start_time = time.time()
200    [cmd, subp] = process_open(args)
201    timeout_limit = int(args.timeout_limit) if args.timeout_limit else 1200  # units: s
202
203    try:
204        out, err = subp.communicate(timeout=timeout_limit)
205    except subprocess.TimeoutExpired:
206        raise RuntimeError('Run [', cmd, '] timeout, timeout_limit = ', timeout_limit, 's')
207
208    out_str = out.decode('UTF-8', errors="ignore")
209    err_str = err.decode('UTF-8', errors="ignore")
210    generate_stub_code_comment(out_str)
211    returncode = str(subp.returncode)
212
213    if args.expect_output:
214        check_expect_output(cmd, returncode, out_str, err_str, args.expect_output)
215    elif args.expect_sub_output:
216        check_expect_sub_output(cmd, returncode, out_str, err_str, args.expect_sub_output)
217    elif args.expect_sub_error:
218        check_expect_sub_error(cmd, returncode, out_str, err_str, args.expect_sub_error)
219    elif args.expect_file:
220        check_expect_file(cmd, returncode, out_str, err_str, args.expect_file)
221    else:
222        raise RuntimeError("Run [" + cmd + "] with no expect !")
223
224    print("Run [" + cmd + "] success!")
225    print("used: %.5f seconds" % (time.time() - start_time))
226
227def compare_line_by_line(expect_output:str, got_output:str):
228    expect_output_list = expect_output.split("\n")
229    got_output_list = got_output.split("\n")
230    for index, (expect_line, got_line) in enumerate(zip(expect_output_list, got_output_list)):
231        if expect_line == got_line:
232            continue
233        error_msg = ""
234
235        if "__INT_MORE_PREV__" in expect_line:
236            prev_got_value = reverse_find_first_not_trace_line(got_output_list, index-1)
237            if got_line.isdigit() and prev_got_value.isdigit() and int(prev_got_value) < int(got_line):
238                continue
239            error_msg = "Got integer result is not more than previous integer result"
240
241        if "__INT__" in expect_line:
242            if got_line.isdigit():
243                continue
244            error_msg = "Got not integer"
245
246        print(">>>>> diff <<<<<")
247        if error_msg:
248            print(error_msg)
249        print("Difference in line {}:\nExcepted: [{}]\nBut got:  [{}]".format(index+1, expect_line, got_line))
250        return True
251    return False
252
253def reverse_find_first_not_trace_line(output_list: list, init_index: int) -> str:
254    for i in range(init_index, -1, -1):
255        if "[trace]" not in output_list[i]:
256            return output_list[i]
257    return ""
258
259if __name__ == '__main__':
260    input_args = parse_args()
261    judge_output(input_args)
262