14514f5e3Sopenharmony_ci#!/usr/bin/env python3
24514f5e3Sopenharmony_ci#coding: utf-8
34514f5e3Sopenharmony_ci
44514f5e3Sopenharmony_ci"""
54514f5e3Sopenharmony_ciCopyright (c) 2021-2022 Huawei Device Co., Ltd.
64514f5e3Sopenharmony_ciLicensed under the Apache License, Version 2.0 (the "License");
74514f5e3Sopenharmony_ciyou may not use this file except in compliance with the License.
84514f5e3Sopenharmony_ciYou may obtain a copy of the License at
94514f5e3Sopenharmony_ci
104514f5e3Sopenharmony_ci    http://www.apache.org/licenses/LICENSE-2.0
114514f5e3Sopenharmony_ci
124514f5e3Sopenharmony_ciUnless required by applicable law or agreed to in writing, software
134514f5e3Sopenharmony_cidistributed under the License is distributed on an "AS IS" BASIS,
144514f5e3Sopenharmony_ciWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
154514f5e3Sopenharmony_ciSee the License for the specific language governing permissions and
164514f5e3Sopenharmony_cilimitations under the License.
174514f5e3Sopenharmony_ci
184514f5e3Sopenharmony_ciDescription: run script
194514f5e3Sopenharmony_ci    expect_output will get run result,
204514f5e3Sopenharmony_ci    expect_sub_output will catch pivotal sub output,
214514f5e3Sopenharmony_ci    expect_file will get print string
224514f5e3Sopenharmony_ci"""
234514f5e3Sopenharmony_ci
244514f5e3Sopenharmony_ciimport argparse
254514f5e3Sopenharmony_ciimport os
264514f5e3Sopenharmony_ciimport stat
274514f5e3Sopenharmony_ciimport subprocess
284514f5e3Sopenharmony_ciimport sys
294514f5e3Sopenharmony_ciimport time
304514f5e3Sopenharmony_ci
314514f5e3Sopenharmony_ci
324514f5e3Sopenharmony_cidef get_env_path_from_rsp(script_file: str) -> list:
334514f5e3Sopenharmony_ci    """get env path from response file recursively."""
344514f5e3Sopenharmony_ci    rsp_file = "{0}{1}".format(script_file, ".rsp")
354514f5e3Sopenharmony_ci    if not os.path.exists(rsp_file):
364514f5e3Sopenharmony_ci        print(
374514f5e3Sopenharmony_ci            "File \"{}\" does not exist!\n" \
384514f5e3Sopenharmony_ci            "This indicates that its related shared_library is not compiled by this project, but there is an " \
394514f5e3Sopenharmony_ci            "executable or shared_library depend on its related shared_library!".format(rsp_file))
404514f5e3Sopenharmony_ci        sys.exit(1)
414514f5e3Sopenharmony_ci
424514f5e3Sopenharmony_ci    rsp_info_list = []
434514f5e3Sopenharmony_ci    with open(rsp_file, "r") as fi:
444514f5e3Sopenharmony_ci        rsp_info_str = fi.read()
454514f5e3Sopenharmony_ci        rsp_info_list = rsp_info_str.split(" ")
464514f5e3Sopenharmony_ci
474514f5e3Sopenharmony_ci    env_path_list = []
484514f5e3Sopenharmony_ci    for element in rsp_info_list:
494514f5e3Sopenharmony_ci        if element.endswith(".so") or element.endswith(".dll"):
504514f5e3Sopenharmony_ci            env_path_list.extend(get_env_path_from_rsp(element))
514514f5e3Sopenharmony_ci            env_path_list.append(os.path.dirname(element))
524514f5e3Sopenharmony_ci    return env_path_list
534514f5e3Sopenharmony_ci
544514f5e3Sopenharmony_ci
554514f5e3Sopenharmony_cidef get_command_and_env_path(args: object) -> [str, str]:
564514f5e3Sopenharmony_ci    """get command and environment path from args for running excutable."""
574514f5e3Sopenharmony_ci    env_path_list = list(set(get_env_path_from_rsp(args.script_file)))
584514f5e3Sopenharmony_ci    env_path_list.append(args.clang_lib_path)
594514f5e3Sopenharmony_ci    env_path = ":".join(env_path_list)
604514f5e3Sopenharmony_ci    if args.qemu_binary_path:
614514f5e3Sopenharmony_ci        if not os.path.exists(args.qemu_binary_path):
624514f5e3Sopenharmony_ci            print("Have you set up environment for running executables with qemu?\n" \
634514f5e3Sopenharmony_ci                "If not, get set-up steps from https://gitee.com/ark_standalone_build/docs ," \
644514f5e3Sopenharmony_ci                " append your build command of ark.py with option \"--clean-continue\"," \
654514f5e3Sopenharmony_ci                " and execute the appended command after setting up the environment.\n" \
664514f5e3Sopenharmony_ci                "If yes, the environment settings for qemu on your host machine may be different from what the link" \
674514f5e3Sopenharmony_ci                " above shows, it is suggested to match your local environment settings with what the link shows.")
684514f5e3Sopenharmony_ci            sys.exit(1)
694514f5e3Sopenharmony_ci        cmd = \
704514f5e3Sopenharmony_ci            "{}".format(args.qemu_binary_path) + \
714514f5e3Sopenharmony_ci            " -L {}".format(args.qemu_ld_prefix) + \
724514f5e3Sopenharmony_ci            " -E LD_LIBRARY_PATH={}".format(env_path) + \
734514f5e3Sopenharmony_ci            " {}".format(args.script_file)
744514f5e3Sopenharmony_ci    else:
754514f5e3Sopenharmony_ci        cmd = "{}".format(args.script_file)
764514f5e3Sopenharmony_ci    cmd += " {}".format(args.script_options) if args.script_options else ""
774514f5e3Sopenharmony_ci    cmd += " {}".format(args.script_args) if args.script_args else ""
784514f5e3Sopenharmony_ci    return [cmd, env_path]
794514f5e3Sopenharmony_ci
804514f5e3Sopenharmony_ci
814514f5e3Sopenharmony_cidef parse_args() -> object:
824514f5e3Sopenharmony_ci    """parse arguments."""
834514f5e3Sopenharmony_ci    parser = argparse.ArgumentParser()
844514f5e3Sopenharmony_ci    parser.add_argument('--script-file', help='execute script file')
854514f5e3Sopenharmony_ci    parser.add_argument('--script-options', help='execute script options')
864514f5e3Sopenharmony_ci    parser.add_argument('--script-args', help='args of script')
874514f5e3Sopenharmony_ci    parser.add_argument('--expect-output', help='expect output')
884514f5e3Sopenharmony_ci    parser.add_argument('--expect-sub-output', help='expect sub output')
894514f5e3Sopenharmony_ci    parser.add_argument('--expect-file', help='expect file')
904514f5e3Sopenharmony_ci    parser.add_argument('--env-path', help='LD_LIBRARY_PATH env')
914514f5e3Sopenharmony_ci    parser.add_argument('--timeout-limit', help='timeout limit')
924514f5e3Sopenharmony_ci    parser.add_argument('--clang-lib-path', help='part for LD_LIBRARY_PATH, it is not in .rsp file')
934514f5e3Sopenharmony_ci    parser.add_argument('--qemu-binary-path', help='path to qemu binary, run executable with qemu if assigned')
944514f5e3Sopenharmony_ci    parser.add_argument('--qemu-ld-prefix', help='elf interpreter prefix')
954514f5e3Sopenharmony_ci    args = parser.parse_args()
964514f5e3Sopenharmony_ci    return args
974514f5e3Sopenharmony_ci
984514f5e3Sopenharmony_ci
994514f5e3Sopenharmony_cidef process_open(args: object) -> [str, object]:
1004514f5e3Sopenharmony_ci    """get command and open subprocess."""
1014514f5e3Sopenharmony_ci    if args.env_path:
1024514f5e3Sopenharmony_ci        # use the given env-path
1034514f5e3Sopenharmony_ci        cmd = args.script_file
1044514f5e3Sopenharmony_ci        cmd += " {}".format(args.script_options) if args.script_options else ""
1054514f5e3Sopenharmony_ci        cmd += " {}".format(args.script_args) if args.script_args else ""
1064514f5e3Sopenharmony_ci        # process for running executable directly
1074514f5e3Sopenharmony_ci        subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1084514f5e3Sopenharmony_ci            env={'LD_LIBRARY_PATH': str(args.env_path)})
1094514f5e3Sopenharmony_ci    else:
1104514f5e3Sopenharmony_ci        # get env-path from response file recursively
1114514f5e3Sopenharmony_ci        [cmd, env_path] = get_command_and_env_path(args)
1124514f5e3Sopenharmony_ci        if args.qemu_binary_path:
1134514f5e3Sopenharmony_ci            # process for running executable with qemu
1144514f5e3Sopenharmony_ci            subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1154514f5e3Sopenharmony_ci        else:
1164514f5e3Sopenharmony_ci            # process for running executable directly
1174514f5e3Sopenharmony_ci            subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1184514f5e3Sopenharmony_ci                env={'LD_LIBRARY_PATH': str(env_path)})
1194514f5e3Sopenharmony_ci    return [cmd, subp]
1204514f5e3Sopenharmony_ci
1214514f5e3Sopenharmony_ci
1224514f5e3Sopenharmony_cidef generate_stub_code_comment(out_str:str):
1234514f5e3Sopenharmony_ci    flags = os.O_WRONLY | os.O_CREAT
1244514f5e3Sopenharmony_ci    mode = stat.S_IWUSR | stat.S_IRUSR
1254514f5e3Sopenharmony_ci    dir_path = './gen/arkcompiler/ets_runtime/'
1264514f5e3Sopenharmony_ci    if not os.path.exists(dir_path):
1274514f5e3Sopenharmony_ci        return
1284514f5e3Sopenharmony_ci    file_path = dir_path + 'stub_code_comment.txt'
1294514f5e3Sopenharmony_ci    fd = os.open(file_path, flags, mode)
1304514f5e3Sopenharmony_ci    with os.fdopen(fd, "w") as code_comment_file:
1314514f5e3Sopenharmony_ci        code_comment_file.write(out_str)
1324514f5e3Sopenharmony_ci
1334514f5e3Sopenharmony_ci
1344514f5e3Sopenharmony_cidef judge_output(args: object):
1354514f5e3Sopenharmony_ci    """run executable and judge is success or not."""
1364514f5e3Sopenharmony_ci    start_time = time.time()
1374514f5e3Sopenharmony_ci    [cmd, subp] = process_open(args)
1384514f5e3Sopenharmony_ci    timeout_limit = int(args.timeout_limit) if args.timeout_limit else 1200  # units: s
1394514f5e3Sopenharmony_ci
1404514f5e3Sopenharmony_ci    try:
1414514f5e3Sopenharmony_ci        out, err = subp.communicate(timeout=timeout_limit)
1424514f5e3Sopenharmony_ci    except subprocess.TimeoutExpired:
1434514f5e3Sopenharmony_ci        raise RuntimeError('Run [', cmd, '] timeout, timeout_limit = ', timeout_limit, 's')
1444514f5e3Sopenharmony_ci
1454514f5e3Sopenharmony_ci    out_str = out.decode('UTF-8', errors="ignore")
1464514f5e3Sopenharmony_ci    err_str = err.decode('UTF-8', errors="ignore")
1474514f5e3Sopenharmony_ci    generate_stub_code_comment(out_str)
1484514f5e3Sopenharmony_ci    returncode = str(subp.returncode)
1494514f5e3Sopenharmony_ci    if args.expect_output:
1504514f5e3Sopenharmony_ci        if returncode != args.expect_output:
1514514f5e3Sopenharmony_ci            print(">>>>> ret <<<<<")
1524514f5e3Sopenharmony_ci            print(returncode)
1534514f5e3Sopenharmony_ci            print(">>>>> out <<<<<")
1544514f5e3Sopenharmony_ci            print(out_str)
1554514f5e3Sopenharmony_ci            print(">>>>> err <<<<<")
1564514f5e3Sopenharmony_ci            print(err_str)
1574514f5e3Sopenharmony_ci            print(">>>>> Expect return: [" + args.expect_output \
1584514f5e3Sopenharmony_ci                + "]\n>>>>> But got: [" + returncode + "]")
1594514f5e3Sopenharmony_ci            raise RuntimeError("Run [" + cmd + "] failed!")
1604514f5e3Sopenharmony_ci    elif args.expect_sub_output:
1614514f5e3Sopenharmony_ci        if out_str.find(args.expect_sub_output) == -1 or returncode != "0":
1624514f5e3Sopenharmony_ci            print(">>>>> ret <<<<<")
1634514f5e3Sopenharmony_ci            print(returncode)
1644514f5e3Sopenharmony_ci            print(">>>>> err <<<<<")
1654514f5e3Sopenharmony_ci            print(err_str)
1664514f5e3Sopenharmony_ci            print(">>>>> Expect contain: [" + args.expect_sub_output \
1674514f5e3Sopenharmony_ci                + "]\n>>>>> But got: [" + out_str + "]")
1684514f5e3Sopenharmony_ci            raise RuntimeError("Run [" + cmd + "] failed!")
1694514f5e3Sopenharmony_ci    elif args.expect_file:
1704514f5e3Sopenharmony_ci        with open(args.expect_file, mode='r') as file:
1714514f5e3Sopenharmony_ci            # skip license header
1724514f5e3Sopenharmony_ci            expect_output = ''.join(file.readlines()[13:])
1734514f5e3Sopenharmony_ci            file.close()
1744514f5e3Sopenharmony_ci            result_cmp = compare_line_by_line(expect_output, out_str)
1754514f5e3Sopenharmony_ci            if result_cmp or returncode != "0":
1764514f5e3Sopenharmony_ci                print(">>>>> ret <<<<<")
1774514f5e3Sopenharmony_ci                print(returncode)
1784514f5e3Sopenharmony_ci                print(">>>>> err <<<<<")
1794514f5e3Sopenharmony_ci                print(err_str)
1804514f5e3Sopenharmony_ci                print(">>>>> Expect {} lines: [{}]\n>>>>> But got {} lines: [{}]".format(
1814514f5e3Sopenharmony_ci                    expect_output.count('\n'), expect_output, out_str.count('\n'), out_str
1824514f5e3Sopenharmony_ci                ))
1834514f5e3Sopenharmony_ci                raise RuntimeError("Run [" + cmd + "] failed!")
1844514f5e3Sopenharmony_ci    else:
1854514f5e3Sopenharmony_ci        raise RuntimeError("Run [" + cmd + "] with no expect !")
1864514f5e3Sopenharmony_ci
1874514f5e3Sopenharmony_ci    print("Run [" + cmd + "] success!")
1884514f5e3Sopenharmony_ci    print("used: %.5f seconds" % (time.time() - start_time))
1894514f5e3Sopenharmony_ci
1904514f5e3Sopenharmony_cidef compare_line_by_line(expect_output:str, got_output:str):
1914514f5e3Sopenharmony_ci    expect_output_list = expect_output.split("\n")
1924514f5e3Sopenharmony_ci    got_output_list = got_output.split("\n")
1934514f5e3Sopenharmony_ci    for index, (expect_line, got_line) in enumerate(zip(expect_output_list, got_output_list)):
1944514f5e3Sopenharmony_ci        if expect_line == got_line:
1954514f5e3Sopenharmony_ci            continue
1964514f5e3Sopenharmony_ci        error_msg = ""
1974514f5e3Sopenharmony_ci
1984514f5e3Sopenharmony_ci        if "__INT_MORE_PREV__" in expect_line:
1994514f5e3Sopenharmony_ci            prev_got_value = reverse_find_first_not_trace_line(got_output_list, index-1)
2004514f5e3Sopenharmony_ci            if got_line.isdigit() and prev_got_value.isdigit() and int(prev_got_value) < int(got_line):
2014514f5e3Sopenharmony_ci                continue
2024514f5e3Sopenharmony_ci            error_msg = "Got integer result is not more than previous integer result"
2034514f5e3Sopenharmony_ci
2044514f5e3Sopenharmony_ci        if "__INT__" in expect_line:
2054514f5e3Sopenharmony_ci            if got_line.isdigit():
2064514f5e3Sopenharmony_ci                continue
2074514f5e3Sopenharmony_ci            error_msg = "Got not integer"
2084514f5e3Sopenharmony_ci
2094514f5e3Sopenharmony_ci        print(">>>>> diff <<<<<")
2104514f5e3Sopenharmony_ci        if error_msg:
2114514f5e3Sopenharmony_ci            print(error_msg)
2124514f5e3Sopenharmony_ci        print("Difference in line {}:\nExcepted: [{}]\nBut got:  [{}]".format(index+1, expect_line, got_line))
2134514f5e3Sopenharmony_ci        return True
2144514f5e3Sopenharmony_ci    return False
2154514f5e3Sopenharmony_ci
2164514f5e3Sopenharmony_cidef reverse_find_first_not_trace_line(output_list: list, init_index: int) -> str:
2174514f5e3Sopenharmony_ci    for i in range(init_index, -1, -1):
2184514f5e3Sopenharmony_ci        if "[trace]" not in output_list[i]:
2194514f5e3Sopenharmony_ci            return output_list[i]
2204514f5e3Sopenharmony_ci    return ""
2214514f5e3Sopenharmony_ci
2224514f5e3Sopenharmony_ciif __name__ == '__main__':
2234514f5e3Sopenharmony_ci    input_args = parse_args()
2244514f5e3Sopenharmony_ci    judge_output(input_args)
225