1 // © 2021 and later: Unicode, Inc. and others. 2 // License & terms of use: https://www.unicode.org/copyright.html 3 4 package com.ibm.icu.dev.test; 5 6 import java.util.ArrayList; 7 import java.util.List; 8 import java.util.Map; 9 import java.util.Map.Entry; 10 import java.util.TreeMap; 11 import java.util.regex.Matcher; 12 import java.util.regex.Pattern; 13 14 /** 15 * “Known issues” manager. 16 * Intended to be shared between ICU, CLDR, &c. 17 * Test frameworks can create an instance of this to manage known issues 18 */ 19 public class UnicodeKnownIssues { 20 /** 21 * From Java 1.8 22 */ accept(T t)23 public interface Consumer<T> { void accept(T t); } 24 25 private Map<String, List<String>> knownIssues = new TreeMap<>(); 26 /** 27 * Max number of lines to show by default (including the "more") 28 * unless -allKnownIssues is given. Must be at least 2. 29 */ 30 public static final int KNOWN_ISSUES_CURTAILMENT = 2; 31 32 /** 33 * true if all issues should be shown, false if they should 34 * be curtailed. 35 */ 36 private boolean allKnownIssues; 37 38 /** 39 * Construct a known issue manager 40 * @param allKnownIssues true if all known issues should be printed, 41 * not curtailed 42 */ UnicodeKnownIssues(boolean allKnownIssues)43 public UnicodeKnownIssues(boolean allKnownIssues) { 44 this.allKnownIssues = allKnownIssues; 45 } 46 47 /** 48 * Base URL for browsing Unicode JIRA 49 */ 50 public static final String UNICODE_JIRA_BROWSE = "https://unicode-org.atlassian.net/browse/"; 51 52 static final Pattern ICU_TICKET_PATTERN = Pattern.compile( 53 "(?i)(?:icu-)?(\\d+)" 54 ); 55 static final Pattern CLDR_TICKET_PATTERN = Pattern.compile( 56 "(?i)cldr(?:bug:)?(?:-)?(\\d+)" 57 ); 58 59 /** 60 * Match all linkable ticket patterns 61 * @see {org.unicode.cldr.util.CLDRURLS#CLDR_TICKET_BROWSE} 62 */ 63 static final Pattern UNICODE_JIRA_PATTERN = Pattern.compile( 64 "(CLDR|ICU)-(\\d+)" 65 ); 66 67 /** 68 * Log the known issue. 69 * Call this from the test framework when logKnownIssue() is called. 70 * 71 * @param path Path to the error, will be returned in the 72 * known issue list 73 * @param ticket A ticket number string. For an ICU ticket, use "ICU-10245". 74 * For a CLDR ticket, use "CLDR-12345". 75 * For compatibility, "1234" -> ICU-1234 and "cldrbug:456" -> CLDR-456 76 * @param comment Additional comment, or null 77 * 78 */ logKnownIssue(String path, String ticket, String comment)79 public void logKnownIssue(String path, String ticket, String comment) { 80 81 StringBuilder descBuf = new StringBuilder(path); 82 83 if (comment != null && comment.length() > 0) { 84 descBuf.append(" (" + comment + ")"); 85 } 86 String description = descBuf.toString(); 87 88 String ticketLink = "Unknown Ticket"; 89 if (ticket != null && ticket.length() > 0) { 90 Matcher matcher = ICU_TICKET_PATTERN.matcher(ticket); 91 if (matcher.matches()) { 92 ticketLink = "ICU-" + matcher.group(1); 93 } else { 94 matcher = CLDR_TICKET_PATTERN.matcher(ticket); 95 if (matcher.matches()) { 96 ticketLink = "CLDR-" + matcher.group(1); 97 } 98 } 99 } 100 101 List<String> lines = knownIssues.get(ticketLink); 102 if (lines == null) { 103 lines = new ArrayList<>(); 104 knownIssues.put(ticketLink, lines); 105 } 106 if (!lines.contains(description)) { 107 lines.add(description); 108 } 109 } 110 111 /** 112 * Print out all known issues to the logFn. 113 * Usage: printKnownIssues(System.out::println) 114 * @param logFn consumer for Strings (e.g. System.out::println) 115 * @return true if (!allKnownIssues) and we had to curtail 116 */ printKnownIssues(Consumer<String> logFn)117 public boolean printKnownIssues(Consumer<String> logFn) { 118 boolean didCurtail = false; 119 if (knownIssues.isEmpty()) { 120 return false; 121 } 122 logFn.accept("\n " + knownIssues.size() + " Known Issues:"); 123 for (Entry<String, List<String>> entry : knownIssues.entrySet()) { 124 String ticketLink = entry.getKey(); 125 if (UNICODE_JIRA_PATTERN.matcher(ticketLink) != null) { 126 logFn.accept(ticketLink + " <" + UNICODE_JIRA_BROWSE + ticketLink + ">"); 127 } else { 128 // Unknown or something else 129 logFn.accept("<" + ticketLink + ">"); 130 } 131 List<String> entries = entry.getValue(); 132 int issuesToShow = entries.size(); 133 if (!allKnownIssues && issuesToShow > KNOWN_ISSUES_CURTAILMENT) { 134 issuesToShow = (KNOWN_ISSUES_CURTAILMENT - 1); 135 } 136 for (int i=0; i<issuesToShow; i++) { 137 logFn.accept(" - " + entries.get(i)); 138 } 139 if (entries.size() > issuesToShow) { 140 didCurtail = true; 141 logFn.accept(" ... and " + 142 (entries.size() - issuesToShow) + " more"); 143 } 144 } 145 return didCurtail; 146 } 147 148 /** 149 * Reset the known issues 150 */ reset()151 public void reset() { 152 knownIssues.clear(); 153 } 154 } 155