• 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
29
30
31def get_env_path_from_rsp(script_file: str) -> list:
32    """get env path from response file recursively."""
33    rsp_file = "{0}{1}".format(script_file, ".rsp")
34    if not os.path.exists(rsp_file):
35        print(
36            "File \"{}\" does not exist!\n" \
37            "This indicates that its related shared_library is not compiled by this project, but there is an " \
38            "executable or shared_library depend on its related shared_library!".format(rsp_file))
39        sys.exit(1)
40
41    rsp_info_list = []
42    with open(rsp_file, "r") as fi:
43        rsp_info_str = fi.read()
44        rsp_info_list = rsp_info_str.split(" ")
45
46    env_path_list = []
47    for element in rsp_info_list:
48        if element.endswith(".so") or element.endswith(".dll"):
49            env_path_list.extend(get_env_path_from_rsp(element))
50            env_path_list.append(os.path.dirname(element))
51    return env_path_list
52
53
54def get_command_and_env_path(args: object) -> [str, str]:
55    """get command and environment path from args for running excutable."""
56    env_path_list = list(set(get_env_path_from_rsp(args.script_file)))
57    env_path_list.append(args.clang_lib_path)
58    env_path = ":".join(env_path_list)
59    if args.qemu_binary_path:
60        if not os.path.exists(args.qemu_binary_path):
61            print("Have you set up environment for running executables with qemu?\n" \
62                "If not, get set-up steps from https://gitee.com/ark_standalone_build/docs ," \
63                " append your build command of ark.py with option \"--clean-continue\"," \
64                " and execute the appended command after setting up the environment.\n" \
65                "If yes, the environment settings for qemu on your host machine may be different from what the link" \
66                " above shows, it is suggested to match your local environment settings with what the link shows.")
67            sys.exit(1)
68        cmd = \
69            "{}".format(args.qemu_binary_path) + \
70            " -L {}".format(args.qemu_ld_prefix) + \
71            " -E LD_LIBRARY_PATH={}".format(env_path) + \
72            " {}".format(args.script_file)
73    else:
74        cmd = "{}".format(args.script_file)
75    cmd += " {}".format(args.script_options) if args.script_options else ""
76    cmd += " {}".format(args.script_args) if args.script_args else ""
77    return [cmd, env_path]
78
79
80def parse_args() -> object:
81    """parse arguments."""
82    parser = argparse.ArgumentParser()
83    parser.add_argument('--script-file', help='execute script file')
84    parser.add_argument('--script-options', help='execute script options')
85    parser.add_argument('--script-args', help='args of script')
86    parser.add_argument('--expect-output', help='expect output')
87    parser.add_argument('--expect-sub-output', help='expect sub output')
88    parser.add_argument('--expect-file', help='expect file')
89    parser.add_argument('--env-path', help='LD_LIBRARY_PATH env')
90    parser.add_argument('--timeout-limit', help='timeout limit')
91    parser.add_argument('--clang-lib-path', help='part for LD_LIBRARY_PATH, it is not in .rsp file')
92    parser.add_argument('--qemu-binary-path', help='path to qemu binary, run executable with qemu if assigned')
93    parser.add_argument('--qemu-ld-prefix', help='elf interpreter prefix')
94    args = parser.parse_args()
95    return args
96
97
98def process_open(args: object) -> [str, object]:
99    """get command and open subprocess."""
100    if args.env_path:
101        # use the given env-path
102        cmd = args.script_file
103        cmd += " {}".format(args.script_options) if args.script_options else ""
104        cmd += " {}".format(args.script_args) if args.script_args else ""
105        # process for running executable directly
106        subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
107            env={'LD_LIBRARY_PATH': str(args.env_path)})
108    else:
109        # get env-path from response file recursively
110        [cmd, env_path] = get_command_and_env_path(args)
111        if args.qemu_binary_path:
112            # process for running executable with qemu
113            subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
114        else:
115            # process for running executable directly
116            subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
117                env={'LD_LIBRARY_PATH': str(env_path)})
118    return [cmd, subp]
119
120
121def judge_output(args: object):
122    """run executable and judge is success or not."""
123    start_time = time.time()
124    [cmd, subp] = process_open(args)
125    timeout_limit = int(args.timeout_limit) if args.timeout_limit else 1200  # units: s
126
127    try:
128        out, err = subp.communicate(timeout=timeout_limit)
129    except subprocess.TimeoutExpired:
130        raise RuntimeError('Run [', cmd, '] timeout, timeout_limit = ', timeout_limit, 's')
131
132    out_str = out.decode('UTF-8', errors="ignore")
133    err_str = err.decode('UTF-8', errors="ignore")
134    returncode = str(subp.returncode)
135    if args.expect_output:
136        if returncode != args.expect_output:
137            print(">>>>> ret <<<<<")
138            print(returncode)
139            print(">>>>> out <<<<<")
140            print(out_str)
141            print(">>>>> err <<<<<")
142            print(err_str)
143            print(">>>>> Expect return: [" + args.expect_output \
144                + "]\n>>>>> But got: [" + returncode + "]")
145            raise RuntimeError("Run [" + cmd + "] failed!")
146    elif args.expect_sub_output:
147        out_str = out.decode('UTF-8', errors="ignore")
148        if out_str.find(args.expect_sub_output) == -1 or returncode != "0":
149            print(">>>>> ret <<<<<")
150            print(returncode)
151            print(">>>>> err <<<<<")
152            print(err_str)
153            print(">>>>> Expect contain: [" + args.expect_sub_output \
154                + "]\n>>>>> But got: [" + out_str + "]")
155            raise RuntimeError("Run [" + cmd + "] failed!")
156    elif args.expect_file:
157        with open(args.expect_file, mode='r') as file:
158            # skip license header
159            expect_output = ''.join(file.readlines()[13:])
160            file.close()
161            out_str = out.decode('UTF-8', errors="ignore")
162            result_cmp = compare_line_by_line(expect_output, out_str)
163            if result_cmp or returncode != "0":
164                print(">>>>> ret <<<<<")
165                print(returncode)
166                print(">>>>> err <<<<<")
167                print(err_str)
168                print(">>>>> Expect {} lines: [{}]\n>>>>> But got {} lines: [{}]".format(
169                    expect_output.count('\n'), expect_output, out_str.count('\n'), out_str
170                ))
171                raise RuntimeError("Run [" + cmd + "] failed!")
172    else:
173        raise RuntimeError("Run [" + cmd + "] with no expect !")
174
175    print("Run [" + cmd + "] success!")
176    print("used: %.5f seconds" % (time.time() - start_time))
177
178def compare_line_by_line(expect_output:str, got_output:str):
179    expect_output_list = expect_output.split("\n")
180    got_output_list = got_output.split("\n")
181    for index, (expect_line, got_line) in enumerate(zip(expect_output_list, got_output_list)):
182        if expect_line == got_line:
183            continue
184        error_msg = ""
185
186        if "__INT_MORE_PREV__" in expect_line:
187            prev_got_value = reverse_find_first_not_trace_line(got_output_list, index-1)
188            if got_line.isdigit() and prev_got_value.isdigit() and int(prev_got_value) < int(got_line):
189                continue
190            error_msg = "Got integer result is not more than previous integer result"
191
192        if "__INT__" in expect_line:
193            if got_line.isdigit():
194                continue
195            error_msg = "Got not integer"
196
197        print(">>>>> diff <<<<<")
198        if error_msg:
199            print(error_msg)
200        print("Difference in line {}:\nExcepted: [{}]\nBut got:  [{}]".format(index+1, expect_line, got_line))
201        return True
202    return False
203
204def reverse_find_first_not_trace_line(output_list: list, init_index: int) -> str:
205    for i in range(init_index, -1, -1):
206        if "[trace]" not in output_list[i]:
207            return output_list[i]
208    return ""
209
210if __name__ == '__main__':
211    input_args = parse_args()
212    judge_output(input_args)
213