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 command += ' >> uconfig_test.log 2>&1' 43 print(command) 44 p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, 45 stderr=subprocess.STDOUT, close_fds=True) 46 stdout, _ = p.communicate() 47 return stdout, p.returncode 48 49def ExtractUConfigNoXXX(uconfig_file): 50 """Parses uconfig.h and returns a list of UCONFIG_NO_XXX labels. 51 Initializes test result structure. 52 53 Args: 54 uconfig_file: common/unicode/uconfig.h as string. 55 56 Returns: 57 List of all UCONFIG_NO_XXX flags found in uconfig.h, initialized test 58 result structure. 59 """ 60 61 uconfig_no_flags_all = [] 62 test_results = {} 63 uconfig_no_regex = r'UCONFIG_NO_[A-Z_]*' 64 65 # Collect all distinct occurences of UCONFIG_NO_XXX matches in uconfig.h. 66 for uconfig_no_flag in re.finditer(uconfig_no_regex, uconfig_file): 67 if uconfig_no_flag.group(0) not in uconfig_no_flags_all: 68 uconfig_no_flags_all.append(uconfig_no_flag.group(0)) 69 70 # All UCONFIG_NO_XXX flags found in uconfig.h come in form of a guarded 71 # definition. Verify the existence, report error if not found. 72 for uconfig_no_flag in uconfig_no_flags_all: 73 uconfig_no_def_regex = r'(?m)#ifndef %s\n#\s+define %s\s+0\n#endif$' % ( 74 uconfig_no_flag, uconfig_no_flag) 75 uconfig_no_def_match = re.search(uconfig_no_def_regex, uconfig_file) 76 if not uconfig_no_def_match: 77 print('No definition for flag %s found!\n' % uconfig_no_flag) 78 sys.exit(1) 79 80 test_results = {f: {'unit_test': False, 'hdr_test': False} for f in uconfig_no_flags_all} 81 test_results['all_flags'] = {'unit_test': False, 'hdr_test' : False} 82 83 return uconfig_no_flags_all, test_results 84 85def BuildAllFlags(uconfig_no_list): 86 """Builds sequence of -Dflag=1 with each flag from the list.""" 87 88 flag_list = ['-D' + uconfig_no + '=1' for uconfig_no in uconfig_no_list] 89 90 return ' '.join(flag_list) 91 92def RunUnitTests(uconfig_no_list, test_results): 93 """Iterates over all flags, sets each individually during ICU configuration 94 and executes the ICU4C unit tests. 95 96 Args: 97 uconfig_no_list: list of all UCONFIG_NO_XXX flags to test with. 98 test_results: dictionary to record test run results. 99 100 Returns: 101 test_results: updated test result entries. 102 """ 103 104 for uconfig_no in uconfig_no_list: 105 _, exit_code = RunCmd( 106 './runConfigureICU Linux CPPFLAGS=\"-D%s=1"' % uconfig_no) 107 if exit_code != 0: 108 print('ICU4C configuration for flag %s failed' % uconfig_no) 109 sys.exit(1) 110 print('Running unit tests with %s set to 1.' % uconfig_no) 111 _, exit_code = RunCmd('make -j2 check') 112 test_results[uconfig_no]['unit_test'] = (exit_code == 0) 113 RunCmd('make clean') 114 115 # Configure ICU with all UCONFIG_NO_XXX flags set to 1 and execute 116 # the ICU4C unit tests. 117 all_unit_test_config_no = BuildAllFlags(uconfig_no_list) 118 _, exit_code = RunCmd( 119 'CPPFLAGS=\"%s\" ./runConfigureICU Linux' % all_unit_test_config_no) 120 if exit_code != 0: 121 print('ICU configuration with all flags set failed') 122 sys.exit(1) 123 print('Running unit tests with all flags set to 1.') 124 _, exit_code = RunCmd('make -j2 check') 125 test_results['all_flags']['unit_test'] = (exit_code == 0) 126 RunCmd('make clean') 127 128 return test_results 129 130def RunHeaderTests(uconfig_no_list, test_results): 131 """Iterates over all flags and executes the header test. 132 133 Args: 134 uconfig_no_list: list of all UCONFIG_NO_XXX flags to test with. 135 test_results: dictionary to record test run results. 136 137 Returns: 138 test_results: updated test result entries. 139 """ 140 141 # Header tests needs different setup. 142 RunCmd('mkdir /tmp/icu_cnfg') 143 out, exit_code = RunCmd('./runConfigureICU Linux --prefix=/tmp/icu_cnfg') 144 if exit_code != 0: 145 print('ICU4C configuration for header test failed!') 146 print(out) 147 sys.exit(1) 148 149 _, exit_code = RunCmd('make -j2 install') 150 if exit_code != 0: 151 print('make install failed!') 152 sys.exit(1) 153 154 for uconfig_no in uconfig_no_list: 155 print('Running header tests with %s set to 1.' % uconfig_no) 156 _, exit_code = RunCmd( 157 'PATH=/tmp/icu_cnfg/bin:$PATH make -C test/hdrtst UCONFIG_NO=\"-D%s=1\" check' % uconfig_no) 158 test_results[uconfig_no]['hdr_test'] = (exit_code == 0) 159 160 all_hdr_test_flags = BuildAllFlags(uconfig_no_list) 161 print('Running header tests with all flags set to 1.') 162 _, exit_code = RunCmd( 163 'PATH=/tmp/icu_cnfg/bin:$PATH make -C test/hdrtst UCONFIG_NO=\"%s\" check' % all_hdr_test_flags) 164 test_results['all_flags']['hdr_test'] = (exit_code == 0) 165 166 return test_results 167 168def main(): 169 # Read the options and determine what to run. 170 run_hdr = False 171 run_unit = False 172 optlist, _ = getopt.getopt(sys.argv[1:], "pu") 173 for o, _ in optlist: 174 if o == "-p": 175 run_hdr = True 176 elif o == "-u": 177 run_unit = True 178 179 os.chdir('icu4c/source') 180 orig_uconfig_file = ReadFile('common/unicode/uconfig.h') 181 182 all_uconfig_no_flags, test_results = ExtractUConfigNoXXX(orig_uconfig_file) 183 if not all_uconfig_no_flags: 184 print('No UCONFIG_NO_XXX flags found!\n') 185 sys.exit(1) 186 187 if run_unit: 188 RunUnitTests( 189 [u for u in all_uconfig_no_flags if u not in excluded_unit_test_flags], 190 test_results) 191 if run_hdr: 192 RunHeaderTests(all_uconfig_no_flags, test_results) 193 194 # Review test results and report any failures. 195 # 'outcome' will be returned by sys.exit(); 0 indicates success, any 196 # other value indicates failure. 197 outcome = 0 198 print('Summary:\n') 199 for uconfig_no in all_uconfig_no_flags: 200 if run_unit and (uconfig_no not in excluded_unit_test_flags): 201 if not test_results[uconfig_no]['unit_test']: 202 outcome = 1 203 print('%s: unit tests fail' % uconfig_no) 204 if run_hdr and not test_results[uconfig_no]['hdr_test']: 205 outcome = 1 206 print('%s: header tests fails' % uconfig_no) 207 if run_unit and not test_results['all_flags']['unit_test']: 208 outcome = 1 209 print('all flags to 1: unit tests fail!') 210 if run_hdr and not test_results['all_flags']['hdr_test']: 211 outcome = 1 212 print('all flags to 1: header tests fail!') 213 if outcome == 0: 214 print('Tests pass for all uconfig variations!') 215 sys.exit(outcome) 216 217 218if __name__ == '__main__': 219 main() 220