• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# coding: utf-8
3#
4# Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without modification,
7# are permitted provided that the following conditions are met:
8#
9# 1. Redistributions of source code must retain the above copyright notice, this list of
10#    conditions and the following disclaimer.
11#
12# 2. Redistributions in binary form must reproduce the above copyright notice, this list
13#    of conditions and the following disclaimer in the documentation and/or other materials
14#    provided with the distribution.
15#
16# 3. Neither the name of the copyright holder nor the names of its contributors may be used
17#    to endorse or promote products derived from this software without specific prior written
18#    permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
24# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32import os
33import platform
34import shutil
35import subprocess
36import sys
37import time
38import base64
39
40SOURCE_ROOT = "../../"
41CMAKE_GEN_PATH = "cmake-build-debug"
42WORK_DIR = ""
43HCGEN = ""
44TEMP_DIR = 'temp'
45ERROR_COLOR_PREFIX = "\033[31m"
46ERROR_COLOR_END = "\033[0m"
47SHOULD_CLEAN_TEMP = True
48PERFORMANCE_MAX_COMPILE_TIME_MS = 1000
49
50
51def text_file_compare(file_a, file_target):
52    if not os.path.exists(file_target):
53        return True
54
55    with open(file_a, 'r') as f_a:
56        with open(file_target, 'r') as f_b:
57            a_content = f_a.read().replace(r'\r\n', r'\n')
58            b_content = f_b.read().replace(r'\r\n', r'\n')
59            return a_content == b_content
60
61
62def binary_file_compare(file_a, file_target, skip_size=0,
63                        target_base64_encode=False):
64    if not os.path.exists(file_target):
65        return True
66
67    with open(file_a, 'rb') as f_a:
68        with open(file_target, 'rb') as f_b:
69            a_content = f_a.read()
70            b_content = f_b.read()
71            if target_base64_encode:
72                b_content = base64.b64decode(b_content)
73            return a_content[skip_size:] == b_content[skip_size:]
74
75
76def exec_command(command):
77    return subprocess.getstatusoutput(command)
78
79
80def setup_hcgen_compiler():
81    global SOURCE_ROOT
82    global CMAKE_GEN_PATH
83    global HCGEN
84
85    if len(sys.argv) > 1:
86        hcgen_path = os.path.abspath(sys.argv[1])
87        if hcgen_path.find('hc-gen') >= 0 and os.access(hcgen_path, os.X_OK):
88            HCGEN = hcgen_path
89            print('use specified hsc:' + hcgen_path)
90            return
91
92    source_root = '../../'
93    compiler_name = "hc-gen"
94    if platform.system() == "Windows":
95        source_root = source_root.replace("/", "\\")
96        compiler_name += ".exe"
97
98    source_root = os.path.abspath(os.path.join(WORK_DIR, source_root))
99    hcgen = os.path.join(source_root, compiler_name)
100    if not os.access(hcgen, os.X_OK):
101        hcgen = os.path.join(source_root, CMAKE_GEN_PATH, compiler_name)
102        if not os.access(hcgen, os.X_OK):
103            print("Error: hcgen not found, please make first")
104            exit(1)
105    HCGEN = hcgen
106
107
108def index_case(case_path):
109    cases = []
110    for dir_name in os.listdir(case_path):
111        if os.path.isdir(os.path.join(case_path, dir_name)):
112            cases.append(dir_name)
113    cases.sort()
114    return cases
115
116
117def get_golden_compile_result(mode, case_name):
118    result_file_name = os.path.join(WORK_DIR, case_name,
119                                    'golden_%s_compile_result.txt' % mode)
120    status_prefix = '[compile exit status]:'
121    output_prefix = '[compile console output]:\n'
122    with open(result_file_name, 'r') as result_file:
123        status = result_file.readline()
124        status = status[len(status_prefix):]
125        console_output = result_file.read()
126        console_output = console_output[len(output_prefix):]
127
128        return int(status), console_output.strip()
129
130
131def compile_status_to_str(status):
132    if status:
133        return 'success'
134    else:
135        return 'failed'
136
137
138def test_compile(case_name, mode):
139    output_dir = os.path.join(WORK_DIR, TEMP_DIR, case_name)
140    if not os.path.exists(output_dir):
141        os.makedirs(output_dir)
142    output_file = os.path.join(output_dir, 'golden')
143    source_file = os.path.join(WORK_DIR, case_name, 'case.hcs')
144    temp_dir = os.path.join(WORK_DIR, TEMP_DIR)
145
146    if mode == 'text':
147        command = "%s -o %s -t  %s" % (HCGEN, output_file, source_file)
148    else:
149        command = "%s -o %s %s" % (HCGEN, output_file, source_file)
150
151    status, output = exec_command(command)
152    golden_status, golden_output = get_golden_compile_result(mode, case_name)
153    if bool(status) != bool(golden_status):
154        print("%s mode: case %s expect compile %s but %s" %
155              (mode, case_name, compile_status_to_str(status),
156               compile_status_to_str(golden_status)))
157        print("Console output :\n" + output)
158        return False
159
160    output = output.replace(temp_dir, ".").replace(WORK_DIR, "."). \
161        replace('\\', '/').replace(ERROR_COLOR_PREFIX, ""). \
162        replace(ERROR_COLOR_END, "")
163    if output.strip() != golden_output:
164        print("output is different with golden for %s compile:" % mode)
165        print("EXPECT:\n" + golden_output)
166        print("ACTUAL:\n" + output.strip())
167        return False
168
169    return True
170
171
172def binary_code_compile(case_name):
173    compile_result = test_compile(case_name, 'binary')
174    if not compile_result:
175        return False
176
177    compile_start_time = get_current_time_ms()
178
179    case_hcb = os.path.join(WORK_DIR, TEMP_DIR, case_name, 'golden.hcb')
180    golden_hcb = os.path.join(WORK_DIR, case_name, 'golden.hcb')
181    hcb_header_size = 20  # hcb compare skip hcb header
182    output_compare = \
183        binary_file_compare(case_hcb, golden_hcb, hcb_header_size, True)
184    if not output_compare:
185        print('Error: hcb output mismatch with golden')
186        return False
187
188    compile_finish_time = get_current_time_ms()
189    compile_used_time = compile_finish_time - compile_start_time
190    if compile_used_time > PERFORMANCE_MAX_COMPILE_TIME_MS:
191        print('Error: compile time %d, out of threshold %d ms'
192              % (compile_used_time, PERFORMANCE_MAX_COMPILE_TIME_MS))
193        return False
194
195    decompile_result = test_decompile(case_name)
196
197    return decompile_result
198
199
200def test_text_code_compile(case_name):
201    compile_result = test_compile(case_name, 'text')
202    if not compile_result:
203        return False
204
205    case_c_file = os.path.join(WORK_DIR, TEMP_DIR, case_name, 'golden.c')
206    golden_c_file = os.path.join(WORK_DIR, case_name, 'golden.c.gen')
207    c_file_compare = text_file_compare(case_c_file, golden_c_file)
208    if not c_file_compare:
209        print("Error: The generated C file mismatch with golden")
210
211    case_header_file = os.path.join(WORK_DIR, TEMP_DIR, case_name, 'golden.h')
212    golden_header_file = os.path.join(WORK_DIR, case_name, 'golden.h.gen')
213    header_file_compare = \
214        text_file_compare(case_header_file, golden_header_file)
215    if not header_file_compare:
216        print("Error: The generated header file mismatch with golden")
217    return c_file_compare and header_file_compare
218
219
220def test_decompile(case_name):
221    golden_decompile_file_name = \
222        os.path.join(WORK_DIR, case_name, 'golden.d.hcs')
223    if not os.path.exists(golden_decompile_file_name):
224        return True
225
226    output_dir = os.path.join(WORK_DIR, TEMP_DIR, case_name)
227    output_file = os.path.join(output_dir, 'case.hcs')
228    source_file = os.path.join(output_dir, 'golden.hcb')
229    command = "%s -o %s -d %s" % (HCGEN, output_file, source_file)
230
231    status, output = exec_command(command)
232    if status != 0:
233        print('decompile fail')
234        print(output)
235        return False
236
237    decompile_golden_result = text_file_compare(
238        os.path.join(output_dir, 'case.d.hcs'), golden_decompile_file_name)
239    if not decompile_golden_result:
240        print('Error: case %s decompile hcs mismatch with golden' % case_name)
241        return False
242
243    return True
244
245
246def get_current_time_ms():
247    return int(round(time.time() * 1000))
248
249
250def test_cases(cases):
251    global SHOULD_CLEAN_TEMP
252    print('[==========] running %d cases form hcgen test' % len(cases))
253    failed_cases = []
254    test_start_time = get_current_time_ms()
255    for case in cases:
256        case_start_time = get_current_time_ms()
257        print('[ RUN      ] %s' % case)
258        binary_compile_result = binary_code_compile(case)
259        text_compile_result = test_text_code_compile(case)
260        case_finish_time = get_current_time_ms()
261        used_time_str = ' (%d ms)' % (case_finish_time - case_start_time)
262        if (not binary_compile_result) or (not text_compile_result):
263            print('[    ERROR ] %s%s' % (case, used_time_str))
264            failed_cases.append(case)
265        else:
266            print('[       OK ] %s%s' % (case, used_time_str))
267    test_finish_time = get_current_time_ms()
268    print('\n[==========] running %d case (%d ms)'
269          % (len(cases), test_finish_time - test_start_time))
270    print('[  PASSED  ] %d cases' % (len(cases) - len(failed_cases)))
271    if len(failed_cases) > 0:
272        SHOULD_CLEAN_TEMP = False
273        print('[  FAILED  ] %d cases, list below:' % len(failed_cases))
274        for case in failed_cases:
275            print('[  FAILED  ] %s' % case)
276
277
278def setup_work_dir():
279    global WORK_DIR
280    pwd = os.path.abspath(sys.argv[0])
281    pwd = pwd[:pwd.rfind(os.sep)]
282    WORK_DIR = pwd
283
284
285def test_setup():
286    temp_dir = os.path.join(WORK_DIR, TEMP_DIR)
287    if not os.path.exists(temp_dir):
288        os.mkdir(temp_dir)
289
290
291def test_teardown():
292    global SHOULD_CLEAN_TEMP
293    if not SHOULD_CLEAN_TEMP:
294        return
295    temp_dir = os.path.join(WORK_DIR, TEMP_DIR)
296    if os.path.exists(temp_dir):
297        shutil.rmtree(temp_dir)
298
299
300def clean_up():
301    temp_dir = os.path.join(WORK_DIR, TEMP_DIR)
302    if os.path.exists(temp_dir):
303        shutil.rmtree(temp_dir)
304
305
306if __name__ == "__main__":
307    setup_work_dir()
308    clean_up()
309    setup_hcgen_compiler()
310    print("hcgen path : " + HCGEN)
311    cases_list = index_case(WORK_DIR)
312    test_setup()
313    test_cases(cases_list)
314    test_teardown()
315