1# © 2021 and later: Unicode, Inc. and others. 2# License & terms of use: http://www.unicode.org/copyright.html 3 4"""Executes uconfig variations check. 5 6See 7http://site.icu-project.org/processes/release/tasks/healthy-code#TOC-Test-uconfig.h-variations 8for more information. 9""" 10 11import getopt 12import os 13import re 14import subprocess 15import sys 16 17excluded_unit_test_flags = ['UCONFIG_NO_CONVERSION', 'UCONFIG_NO_FILE_IO']; 18 19def ReadFile(filename): 20 """Reads a file and returns the content of the file 21 22 Args: 23 command: string with the filename. 24 25 Returns: 26 Content of file. 27 """ 28 29 with open(filename, 'r') as file_handle: 30 return file_handle.read() 31 32def RunCmd(command): 33 """Executes the command, returns output and exit code, writes output to log 34 35 Args: 36 command: string with the command. 37 38 Returns: 39 stdout and exit code of command execution. 40 """ 41 42 print(command) 43 p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, 44 stderr=subprocess.STDOUT, close_fds=True) 45 stdout, _ = p.communicate() 46 return stdout, p.returncode 47 48def ExtractUConfigNoXXX(uconfig_file): 49 """Parses uconfig.h and returns a list of UCONFIG_NO_XXX labels. 50 Initializes test result structure. 51 52 Args: 53 uconfig_file: common/unicode/uconfig.h as string. 54 55 Returns: 56 List of all UCONFIG_NO_XXX flags found in uconfig.h, initialized test 57 result structure. 58 """ 59 60 uconfig_no_flags_all = [] 61 test_results = {} 62 uconfig_no_regex = r'UCONFIG_NO_[A-Z_]*' 63 64 # Collect all distinct occurences of UCONFIG_NO_XXX matches in uconfig.h. 65 for uconfig_no_flag in re.finditer(uconfig_no_regex, uconfig_file): 66 if uconfig_no_flag.group(0) not in uconfig_no_flags_all: 67 uconfig_no_flags_all.append(uconfig_no_flag.group(0)) 68 69 # All UCONFIG_NO_XXX flags found in uconfig.h come in form of a guarded 70 # definition. Verify the existence, report error if not found. 71 for uconfig_no_flag in uconfig_no_flags_all: 72 uconfig_no_def_regex = r'(?m)#ifndef %s\n#\s+define %s\s+0\n#endif$' % ( 73 uconfig_no_flag, uconfig_no_flag) 74 uconfig_no_def_match = re.search(uconfig_no_def_regex, uconfig_file) 75 if not uconfig_no_def_match: 76 print('No definition for flag %s found!\n' % uconfig_no_flag) 77 sys.exit(1) 78 79 test_results = {f: {'unit_test': False, 'hdr_test': False, 'u_log_tail': '', 'h_log_tail': ''} 80 for f in uconfig_no_flags_all} 81 test_results['all_flags'] = {'unit_test': False, 'hdr_test' : False, 'u_log_tail': '', 82 'h_log_tail': ''} 83 84 return uconfig_no_flags_all, test_results 85 86def BuildAllFlags(uconfig_no_list): 87 """Builds sequence of -Dflag=1 with each flag from the list.""" 88 89 flag_list = ['-D' + uconfig_no + '=1' for uconfig_no in uconfig_no_list] 90 91 return ' '.join(flag_list) 92 93def RunUnitTests(uconfig_no_list, test_results): 94 """Iterates over all flags, sets each individually during ICU configuration 95 and executes the ICU4C unit tests. 96 97 Args: 98 uconfig_no_list: list of all UCONFIG_NO_XXX flags to test with. 99 test_results: dictionary to record test run results. 100 101 Returns: 102 test_results: updated test result entries. 103 """ 104 105 for uconfig_no in uconfig_no_list: 106 conf_log, exit_code = RunCmd( 107 './runConfigureICU Linux CPPFLAGS=\"-D%s=1"' % uconfig_no) 108 if exit_code != 0: 109 print('ICU4C configuration for flag %s failed' % uconfig_no) 110 print('Last 25 lines:\n%s\n' % '\n'.join(conf_log.decode('utf-8').splitlines()[-25:])) 111 sys.exit(1) 112 print('Running unit tests with %s set to 1.' % uconfig_no) 113 run_log, exit_code = RunCmd('make -j2 check') 114 test_results[uconfig_no]['unit_test'] = (exit_code == 0) 115 test_results[uconfig_no]['u_log_tail'] = \ 116 '\n'.join(run_log.decode('utf-8').splitlines()[-50:]) 117 RunCmd('make clean') 118 119 # Configure ICU with all UCONFIG_NO_XXX flags set to 1 and execute 120 # the ICU4C unit tests. 121 all_unit_test_config_no = BuildAllFlags(uconfig_no_list) 122 conf_log, exit_code = RunCmd( 123 'CPPFLAGS=\"%s\" ./runConfigureICU Linux' % all_unit_test_config_no) 124 if exit_code != 0: 125 print('ICU configuration with all flags set failed') 126 print('Last 25 lines:\n%s\n' % '\n'.join(conf_log.decode('utf-8').splitlines()[-25:])) 127 sys.exit(1) 128 print('Running unit tests with all flags set to 1.') 129 run_log, exit_code = RunCmd('make -j2 check') 130 test_results['all_flags']['unit_test'] = (exit_code == 0) 131 test_results['all_flags']['u_log_tail'] = \ 132 '\n'.join(run_log.decode('utf-8').splitlines()[-50:]) 133 RunCmd('make clean') 134 135 return test_results 136 137def RunHeaderTests(uconfig_no_list, test_results): 138 """Iterates over all flags and executes the header test. 139 140 Args: 141 uconfig_no_list: list of all UCONFIG_NO_XXX flags to test with. 142 test_results: dictionary to record test run results. 143 144 Returns: 145 test_results: updated test result entries. 146 """ 147 148 # Header tests needs different setup. 149 RunCmd('mkdir /tmp/icu_cnfg') 150 conf_log, exit_code = RunCmd('./runConfigureICU Linux --prefix=/tmp/icu_cnfg') 151 if exit_code != 0: 152 print('ICU4C configuration for header test failed!') 153 print('Last 25 lines:\n%s\n' % '\n'.join(conf_log.decode('utf-8').splitlines()[-25:])) 154 sys.exit(1) 155 156 inst_log, exit_code = RunCmd('make -j2 install') 157 if exit_code != 0: 158 print('make install failed!') 159 print('Last 25 lines:\n%s\n' % '\n'.join(inst_log.decode('utf-8').splitlines()[-25:])) 160 sys.exit(1) 161 162 for uconfig_no in uconfig_no_list: 163 print('Running header tests with %s set to 1.' % uconfig_no) 164 run_log, exit_code = RunCmd( 165 'PATH=/tmp/icu_cnfg/bin:$PATH make -C test/hdrtst UCONFIG_NO=\"-D%s=1\" check' % uconfig_no) 166 test_results[uconfig_no]['hdr_test'] = (exit_code == 0) 167 test_results[uconfig_no]['h_log_tail'] = \ 168 '\n'.join(run_log.decode('utf-8').splitlines()[-50:]) 169 170 all_hdr_test_flags = BuildAllFlags(uconfig_no_list) 171 print('Running header tests with all flags set to 1.') 172 run_log, exit_code = RunCmd( 173 'PATH=/tmp/icu_cnfg/bin:$PATH make -C test/hdrtst UCONFIG_NO=\"%s\" check' % all_hdr_test_flags) 174 test_results['all_flags']['hdr_test'] = (exit_code == 0) 175 test_results['all_flags']['h_log_tail'] = \ 176 '\n'.join(run_log.decode('utf-8').splitlines()[-50:]) 177 178 return test_results 179 180def main(): 181 # Read the options and determine what to run. 182 run_hdr = False 183 run_unit = False 184 optlist, _ = getopt.getopt(sys.argv[1:], "pu") 185 for o, _ in optlist: 186 if o == "-p": 187 run_hdr = True 188 elif o == "-u": 189 run_unit = True 190 191 os.chdir('icu4c/source') 192 orig_uconfig_file = ReadFile('common/unicode/uconfig.h') 193 194 all_uconfig_no_flags, test_results = ExtractUConfigNoXXX(orig_uconfig_file) 195 if not all_uconfig_no_flags: 196 print('No UCONFIG_NO_XXX flags found!\n') 197 sys.exit(1) 198 199 if run_unit: 200 RunUnitTests( 201 [u for u in all_uconfig_no_flags if u not in excluded_unit_test_flags], 202 test_results) 203 if run_hdr: 204 RunHeaderTests(all_uconfig_no_flags, test_results) 205 206 # Review test results and report any failures. 207 # 'outcome' will be returned by sys.exit(); 0 indicates success, any 208 # other value indicates failure. 209 outcome = 0 210 print('Summary:\n') 211 for uconfig_no in all_uconfig_no_flags: 212 if run_unit and (uconfig_no not in excluded_unit_test_flags): 213 if not test_results[uconfig_no]['unit_test']: 214 outcome = 1 215 print('\n============================================================\n') 216 print('%s: unit tests fail' % uconfig_no) 217 print('Last 50 lines from log file:\n%s\n' % test_results[uconfig_no]['u_log_tail']) 218 print('\n============================================================\n') 219 if run_hdr and not test_results[uconfig_no]['hdr_test']: 220 outcome = 1 221 print('\n============================================================\n') 222 print('%s: header tests fails' % uconfig_no) 223 print('Last 50 lines from log file:\n%s\n' % test_results[uconfig_no]['h_log_tail']) 224 print('\n============================================================\n') 225 226 if run_unit and not test_results['all_flags']['unit_test']: 227 outcome = 1 228 print('\n============================================================\n') 229 print('all flags to 1: unit tests fail!') 230 print('Last 50 lines from log file:\n%s\n' % test_results['all_flags']['u_log_tail']) 231 print('\n============================================================\n') 232 if run_hdr and not test_results['all_flags']['hdr_test']: 233 outcome = 1 234 print('\n============================================================\n') 235 print('all flags to 1: header tests fail!') 236 print('Last 50 lines from log file: %s\n ' % test_results['all_flags']['h_log_tail']) 237 print('\n============================================================\n') 238 if outcome == 0: 239 print('Tests pass for all uconfig variations!') 240 sys.exit(outcome) 241 242 243if __name__ == '__main__': 244 main() 245