--- /dev/null
+# © 2021 and later: Unicode, Inc. and others.
+# License & terms of use: http://www.unicode.org/copyright.html
+
+"""Executes uconfig variations check.
+
+See
+http://site.icu-project.org/processes/release/tasks/healthy-code#TOC-Test-uconfig.h-variations
+for more information.
+"""
+
+import getopt
+import os
+import re
+import subprocess
+import sys
+
+excluded_unit_test_flags = ['UCONFIG_NO_CONVERSION', 'UCONFIG_NO_FILE_IO'];
+
+def ReadFile(filename):
+ """Reads a file and returns the content of the file
+
+ Args:
+ command: string with the filename.
+
+ Returns:
+ Content of file.
+ """
+
+ with open(filename, 'r') as file_handle:
+ return file_handle.read()
+
+def RunCmd(command):
+ """Executes the command, returns output and exit code, writes output to log
+
+ Args:
+ command: string with the command.
+
+ Returns:
+ stdout and exit code of command execution.
+ """
+
+ command += ' >> uconfig_test.log 2>&1'
+ print(command)
+ p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, close_fds=True)
+ stdout, _ = p.communicate()
+ return stdout, p.returncode
+
+def ExtractUConfigNoXXX(uconfig_file):
+ """Parses uconfig.h and returns a list of UCONFIG_NO_XXX labels.
+ Initializes test result structure.
+
+ Args:
+ uconfig_file: common/unicode/uconfig.h as string.
+
+ Returns:
+ List of all UCONFIG_NO_XXX flags found in uconfig.h, initialized test
+ result structure.
+ """
+
+ uconfig_no_flags_all = []
+ test_results = {}
+ uconfig_no_regex = r'UCONFIG_NO_[A-Z_]*'
+
+ # Collect all distinct occurences of UCONFIG_NO_XXX matches in uconfig.h.
+ for uconfig_no_flag in re.finditer(uconfig_no_regex, uconfig_file):
+ if uconfig_no_flag.group(0) not in uconfig_no_flags_all:
+ uconfig_no_flags_all.append(uconfig_no_flag.group(0))
+
+ # All UCONFIG_NO_XXX flags found in uconfig.h come in form of a guarded
+ # definition. Verify the existence, report error if not found.
+ for uconfig_no_flag in uconfig_no_flags_all:
+ uconfig_no_def_regex = r'(?m)#ifndef %s\n#\s+define %s\s+0\n#endif$' % (
+ uconfig_no_flag, uconfig_no_flag)
+ uconfig_no_def_match = re.search(uconfig_no_def_regex, uconfig_file)
+ if not uconfig_no_def_match:
+ print('No definition for flag %s found!\n' % uconfig_no_flag)
+ sys.exit(1)
+
+ test_results = {f: {'unit_test': False, 'hdr_test': False} for f in uconfig_no_flags_all}
+ test_results['all_flags'] = {'unit_test': False, 'hdr_test' : False}
+
+ return uconfig_no_flags_all, test_results
+
+def BuildAllFlags(uconfig_no_list):
+ """Builds sequence of -Dflag=1 with each flag from the list."""
+
+ flag_list = ['-D' + uconfig_no + '=1' for uconfig_no in uconfig_no_list]
+
+ return ' '.join(flag_list)
+
+def RunUnitTests(uconfig_no_list, test_results):
+ """Iterates over all flags, sets each individually during ICU configuration
+ and executes the ICU4C unit tests.
+
+ Args:
+ uconfig_no_list: list of all UCONFIG_NO_XXX flags to test with.
+ test_results: dictionary to record test run results.
+
+ Returns:
+ test_results: updated test result entries.
+ """
+
+ for uconfig_no in uconfig_no_list:
+ _, exit_code = RunCmd(
+ './runConfigureICU Linux CPPFLAGS=\"-D%s=1"' % uconfig_no)
+ if exit_code != 0:
+ print('ICU4C configuration for flag %s failed' % uconfig_no)
+ sys.exit(1)
+ print('Running unit tests with %s set to 1.' % uconfig_no)
+ _, exit_code = RunCmd('make -j2 check')
+ test_results[uconfig_no]['unit_test'] = (exit_code == 0)
+ RunCmd('make clean')
+
+ # Configure ICU with all UCONFIG_NO_XXX flags set to 1 and execute
+ # the ICU4C unit tests.
+ all_unit_test_config_no = BuildAllFlags(uconfig_no_list)
+ _, exit_code = RunCmd(
+ 'CPPFLAGS=\"%s\" ./runConfigureICU Linux' % all_unit_test_config_no)
+ if exit_code != 0:
+ print('ICU configuration with all flags set failed')
+ sys.exit(1)
+ print('Running unit tests with all flags set to 1.')
+ _, exit_code = RunCmd('make -j2 check')
+ test_results['all_flags']['unit_test'] = (exit_code == 0)
+ RunCmd('make clean')
+
+ return test_results
+
+def RunHeaderTests(uconfig_no_list, test_results):
+ """Iterates over all flags and executes the header test.
+
+ Args:
+ uconfig_no_list: list of all UCONFIG_NO_XXX flags to test with.
+ test_results: dictionary to record test run results.
+
+ Returns:
+ test_results: updated test result entries.
+ """
+
+ # Header tests needs different setup.
+ RunCmd('mkdir /tmp/icu_cnfg')
+ out, exit_code = RunCmd('./runConfigureICU Linux --prefix=/tmp/icu_cnfg')
+ if exit_code != 0:
+ print('ICU4C configuration for header test failed!')
+ print(out)
+ sys.exit(1)
+
+ _, exit_code = RunCmd('make -j2 install')
+ if exit_code != 0:
+ print('make install failed!')
+ sys.exit(1)
+
+ for uconfig_no in uconfig_no_list:
+ print('Running header tests with %s set to 1.' % uconfig_no)
+ _, exit_code = RunCmd(
+ 'PATH=/tmp/icu_cnfg/bin:$PATH make -C test/hdrtst UCONFIG_NO=\"-D%s=1\" check' % uconfig_no)
+ test_results[uconfig_no]['hdr_test'] = (exit_code == 0)
+
+ all_hdr_test_flags = BuildAllFlags(uconfig_no_list)
+ print('Running header tests with all flags set to 1.')
+ _, exit_code = RunCmd(
+ 'PATH=/tmp/icu_cnfg/bin:$PATH make -C test/hdrtst UCONFIG_NO=\"%s\" check' % all_hdr_test_flags)
+ test_results['all_flags']['hdr_test'] = (exit_code == 0)
+
+ return test_results
+
+def main():
+ # Read the options and determine what to run.
+ run_hdr = False
+ run_unit = False
+ optlist, _ = getopt.getopt(sys.argv[1:], "pu")
+ for o, _ in optlist:
+ if o == "-p":
+ run_hdr = True
+ elif o == "-u":
+ run_unit = True
+
+ os.chdir('icu4c/source')
+ orig_uconfig_file = ReadFile('common/unicode/uconfig.h')
+
+ all_uconfig_no_flags, test_results = ExtractUConfigNoXXX(orig_uconfig_file)
+ if not all_uconfig_no_flags:
+ print('No UCONFIG_NO_XXX flags found!\n')
+ sys.exit(1)
+
+ if run_unit:
+ RunUnitTests(
+ [u for u in all_uconfig_no_flags if u not in excluded_unit_test_flags],
+ test_results)
+ if run_hdr:
+ RunHeaderTests(all_uconfig_no_flags, test_results)
+
+ # Review test results and report any failures.
+ # 'outcome' will be returned by sys.exit(); 0 indicates success, any
+ # other value indicates failure.
+ outcome = 0
+ print('Summary:\n')
+ for uconfig_no in all_uconfig_no_flags:
+ if run_unit and (uconfig_no not in excluded_unit_test_flags):
+ if not test_results[uconfig_no]['unit_test']:
+ outcome = 1
+ print('%s: unit tests fail' % uconfig_no)
+ if run_hdr and not test_results[uconfig_no]['hdr_test']:
+ outcome = 1
+ print('%s: header tests fails' % uconfig_no)
+ if run_unit and not test_results['all_flags']['unit_test']:
+ outcome = 1
+ print('all flags to 1: unit tests fail!')
+ if run_hdr and not test_results['all_flags']['hdr_test']:
+ outcome = 1
+ print('all flags to 1: header tests fail!')
+ if outcome == 0:
+ print('Tests pass for all uconfig variations!')
+ sys.exit(outcome)
+
+
+if __name__ == '__main__':
+ main()