diff --git a/schema/check_test_output.py b/schema/check_test_output.py index fb5fadf6..68a6a5da 100644 --- a/schema/check_test_output.py +++ b/schema/check_test_output.py @@ -55,7 +55,7 @@ def main(args): test_type = schema_files.TEST_FILE_TO_TEST_TYPE_MAP[test_file_prefix] test_type_set.add(test_type) except BaseException as err: - logging.error('!!! %s for file %s', err, file + logging.info('No file (%s) during schema check output: %s', file, err ) for dir in icu_dirs: icu_version_set.add(os.path.basename(dir)) diff --git a/testdriver/ddtargs.py b/testdriver/ddtargs.py index 4ebdc317..6a2a07bb 100644 --- a/testdriver/ddtargs.py +++ b/testdriver/ddtargs.py @@ -60,8 +60,8 @@ def __init__(self, args): self.parser.add_argument('--custom_verifier', default=None) # self.parser.add_argument( - '--run_serially', default=None, - help='Execute tests in series rather than in parallel') + '--run_serial', default=None, + help='Set if execution should be done serially. Parallel is the default.') self.options = self.parser.parse_args(args) @@ -93,6 +93,9 @@ def __init__(self, args): self.parser.add_argument('--test_verifier', help='Flag to run in test mode', default=None) + self.parser.add_argument('--run_serial', default=None, + help='Set if execution should be done serially. Parallel is the default.') + self.options = self.parser.parse_args(args) return diff --git a/testdriver/testdriver.py b/testdriver/testdriver.py index 95e5c893..a696acfd 100644 --- a/testdriver/testdriver.py +++ b/testdriver/testdriver.py @@ -29,7 +29,7 @@ def __init__(self): self.test_plans = [] self.debug = False - self.run_serially = False # Default is to operate in parallel + self.run_serial = False # Default is to operate in parallel logging.config.fileConfig("../logging.conf") @@ -40,7 +40,7 @@ def set_args(self, arg_options): self.icuVersion = arg_options.icu_version self.cldrVersion = arg_options.cldr - self.run_serially = arg_options.run_serially + self.run_serial = arg_options.run_serial # Create "test plans" for each option for test_type in arg_options.test_type: @@ -125,7 +125,7 @@ def main(args): # print('ARGS = %s' % (args)) driver.parse_args(args[1:]) - if driver.run_serially: + if driver.run_serial: driver.run_plans() else: driver.run_plans_parallel() diff --git a/verifier/check_known_issues.py b/verifier/check_known_issues.py new file mode 100644 index 00000000..f5e59cec --- /dev/null +++ b/verifier/check_known_issues.py @@ -0,0 +1,172 @@ +# Functions to handle Known Issue category of results + + +from report_template import reportTemplate + +from collections import defaultdict +from enum import Enum + +from difflib import HtmlDiff +from difflib import Differ +from difflib import SequenceMatcher + +from datetime import datetime + +import glob +import json +import logging +import logging.config +import os +from string import Template +import sys + +sys.path.append('../testdriver') +import datasets as ddt_data + +# For information on characters and scripts +import unicodedata + +# Handle known issues database + +# Automatically compute some known issues with certain patterns of differences +# in actual output vs. expected + +# E.g., NBSP vs SP in NodeJS DateTime in ICU73, ... + +# Constants +NBSP = '\u202f' +SP = '\u0020' + + +# Global KnownIssue Info types and strings +class knownIssueType(Enum): + known_issue_nbsp_sp = 'ASCII Space instead of NBSP' + known_issue_replaced_numerals = 'Not creating non-ASCII numerals' + +# TODO! Load known issues from file of known problems rather than hardcoding the detection in each test + +# Tests for specific kinds of known issues +def diff_nbsp_vs_ascii_space(actual, expected_value): + # Returns the ID of this if the only difference in the two strings + # is Narrow Non-breaking Space (NBSP) in expected vs. ASCII space in the actual result. + # Found in datetime testing. + if not expected_value or not actual: + return None + + # If replacing all the NBSP characdters in expected gives the actual result, + # then the only differences were with this type of space in formatted output. + if expected_value.replace(NBSP, SP) == actual: + return knownIssueType.known_issue_nbsp_sp + else: + return None + + +def numerals_replaced_by_another_numbering_system(expected, actual): + # If the only difference are one type of digit + # where other digits were expected, return True + # Found in datetime testing. + # Returns an known issue ID (or string) if the the numbering system changed + + # sm_opcodes describe the change to turn expected string into the actual string + # See https://docs.python.org/3/library/difflib.html#difflib.SequenceMatcher.get_opcodes + sm = SequenceMatcher(None, expected, actual) + sm_opcodes = sm.get_opcodes() + + digit_replace = False + non_digit_replacement = False + + # sm_opcodes describe the changes to turn expected string into the actual string + # See https://docs.python.org/3/library/difflib.html#difflib.SequenceMatcher.get_opcodes + # The tuple is [tag, i1, i2, j1, j2] + # Tag indicates the type of change. + # i1:i2 is the range of the substring in expected + # j1:j2 is the range of the substring in actual + + for diff in sm_opcodes: + tag = diff[0] # 'replace', 'delete', 'insert', or 'equal' + old_val = expected[diff[1]:diff[2]] + new_val = actual[diff[3]:diff[4]] + if tag == 'replace': + # expected[i1:i2] was replaced by actual[j1:j2] + if old_val.isdigit() and new_val.isdigit(): + # TODO!! : check the value of the numeral + # If the same value, then its a numbering system difference + if unicodedata.numeric(old_val) == unicodedata.numeric(new_val): + digit_replace = True + else: + # Both were digits but different numeric values + non_digit_replacement = True + else: + # a digit was replaced with a non-digit + non_digit_replacement = True + + # Only true if the only changes were replacing digits + if digit_replace and not non_digit_replace: + return knownIssueType.known_issue_replaced_numerals + else: + return None + + +def check_datetime_known_issues(test): + # Examine a single test for date/time isses + # Returns known issues identified for this test in this category + remove_this_one = False + try: + result = test['result'] + expected = test['expected'] + is_ki = diff_nbsp_vs_ascii_space(result, expected) + if is_ki: + # Mark the test with this issue + test['known_issue'] = knownIssueType.known_issue_nbsp_sp.value + remove_this_one = True + + is_ki = numerals_replaced_by_another_numbering_system(result, expected) + if is_ki: + test['known_issue_id'] = knownIssueType.known_issue_replaced_numerals.value + remove_this_one = True + + except BaseException as err: + # Can't get the info + pass + + return remove_this_one + + +def compute_known_issues_for_single_test(test_type, test): + # Based on the type of test, check known issues against the expected vs. actual + # results + + # Returns True if this single test is an example of one or moore known issues, + known_issue_found = False + if test_type == ddt_data.testType.datetime_fmt.value: + known_issue_found = check_datetime_known_issues(test) + + # TODO: Add checks here for known issues in other test types + + return known_issue_found + +def check_issues(test_type, test_results_to_check): + # Look at the array of test result types, failure, error, unsupported + # Extract any tests from these that are known issues + # Return the list of tests that are known issues + # + known_issues_list = [] + + for category in test_results_to_check: + test_indices_with_known_issues = set() + index = 0 + + for test in category: + is_known_issue = compute_known_issues_for_single_test(test_type, test) + if is_known_issue: + known_issues_list.append(test) + test_indices_with_known_issues.add(index) + index += 1 + + # Remove those that were marked as known issues + # Reverse order to not confuse the position while deleting + rev_indices = sorted(test_indices_with_known_issues, reverse=True) + for index in rev_indices: + del category[index] + + return known_issues_list diff --git a/verifier/detail_template.html b/verifier/detail_template.html index 9c89cae4..2cad2d56 100644 --- a/verifier/detail_template.html +++ b/verifier/detail_template.html @@ -34,7 +34,7 @@ font-size:20px; } - #diff_area { + .diff_area { font-size: 24px; font-color: blue; } @@ -231,9 +231,9 @@ ['Results', 'Count', {role:'style'}], ['Passing', test_results['pass'].json.length, '#44ff77'], ['Failing', test_results['fail'].json.length, '#ff0000'], + ['Known issues', test_results['known_issue'].json.length, '#ff8200'], ['Errors', test_results['error'].json.length, '#ffdd00'], - ['Unsupported', test_results['unsupported'].json.length, '#777777'], - ['Known issues', test_results['known_issue'].json.length, '#ff8200'] + ['Unsupported', test_results['unsupported'].json.length, '#777777'] ]; const chart = new google.visualization.BarChart(chart_output_area); let chart_data = google.visualization.arrayToDataTable(input_data); @@ -347,13 +347,19 @@ fill_pagination("#characterized-pagination-container_" + item_type, "#characterized-data-container_" + item_type, selected_json_data, - "selected_" + item_type); + item_type); } // UL Template for pagination.js function simpleTemplating(data, c_type) { let possible_fields = ['label', 'expected', 'result', 'error', 'error_detail', 'options', 'input_data', 'actual_options']; + // Support output to different regions, depending on the type + // of the data, e.g., "fail", "known_issue", etc. + const diff_area_name = "diff_area_" + c_type; + const onclick_call = + '"captureInputDataOnClick(this);" onmouseover="hoverDiffText(this,' + diff_area_name + ');"'; + let table_opening = '
' + output +' | '); + html.push('' + output +' | '); } } html.push(""); @@ -637,8 +643,9 @@ navigator.clipboard.writeText(output); } - // On hover, show the differen between expected and actual result. - function hoverDiffText(element) { + // On hover, show the difference between expected and actual result + // in the named area. + function hoverDiffText(element, diff_area) { // First, get the row of this item. const row = element.parentNode; const text1 = row.children[1].innerHTML; @@ -651,7 +658,6 @@ dmp.diff_cleanupSemantic(diff_text); // And show the difference nicely. const ds = dmp.diff_prettyHtml(diff_text); - const diff_area = document.getElementById("diff_area"); diff_area.innerHTML = ds; } @@ -672,7 +678,7 @@