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