ICU-21756 icu4j: port UnicodeKnownIssues.java from CLDR

- port of CLDR-14588
- use a fake Consumer<String>
- currently logs after each test class, not ideal but better
- Formerly ICU-12589 but that is not as related
- add unit test
This commit is contained in:
Steven R. Loomis 2021-09-17 19:51:23 -05:00
parent f5cc0c43d6
commit 7bc2009f7f
3 changed files with 313 additions and 36 deletions

View file

@ -0,0 +1,143 @@
// © 2021 and later: Unicode, Inc. and others.
// License & terms of use: https://www.unicode.org/copyright.html
package com.ibm.icu.dev.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.LinkedList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Test for {@link UnicodeKnownIssues}
*/
@RunWith(JUnit4.class)
public class TestUnicodeKnownIssues {
@Test
public void TestBasic() {
UnicodeKnownIssues uki = new UnicodeKnownIssues(true);
uki.logKnownIssue("a/b/c", "ICU-21756", "Something is working!");
List<String> l = printToList(uki);
assertNotNull("no list", l);
assertEquals("message count Of Three", 3, l.size());
assertTrue(l.get(0) + "#0 says Known Issues", l.get(0).contains("Known Issues"));
assertTrue(l.get(1) + "#1 has atlassian URL", l.get(1).contains("browse/ICU-21756"));
assertTrue(l.get(2) + "#2 says a/b/c", l.get(2).contains("a/b/c"));
}
@Test
public void TestNotCurtailed() {
UnicodeKnownIssues uki = new UnicodeKnownIssues(true);
uki.logKnownIssue("a/b/c", "ICU-21756", "Something is working!");
uki.logKnownIssue("d/e/f", "ICU-21756", "Something is working!");
uki.logKnownIssue("g/h/i", "ICU-21756", "Something is working!");
List<String> l = printToList(uki);
assertNotNull("no list", l);
assertEquals("message count Of 5", 5, l.size());
assertTrue(l.get(0) + "#0 says Known Issues", l.get(0).contains("Known Issues"));
assertTrue(l.get(1) + "#1 has atlassian URL", l.get(1).contains("browse/ICU-21756"));
// Not curtailed: test shows up in a/b/c, d/e/f, and g/h/i
assertTrue(l.get(2) + "#2 says a/b/c", l.get(2).contains("a/b/c"));
assertTrue(l.get(3) + "#3 says d/e/f", l.get(3).contains("d/e/f"));
assertTrue(l.get(4) + "#4 says g/h/i", l.get(4).contains("g/h/i"));
}
@Test
public void TestCurtailed() {
UnicodeKnownIssues uki = new UnicodeKnownIssues(false);
uki.logKnownIssue("a/b/c", "ICU-21756", "Something is working!");
uki.logKnownIssue("d/e/f", "ICU-21756", "Something is working!");
uki.logKnownIssue("g/h/i", "ICU-21756", "Something is working!");
List<String> l = printToList(uki);
assertNotNull("no list", l);
assertEquals("message count", 4, l.size());
assertTrue(l.get(0) + "#0 says Known Issues", l.get(0).contains("Known Issues"));
assertTrue(l.get(1) + "#1 has atlassian URL", l.get(1).contains("browse/ICU-21756"));
assertTrue(l.get(2) + "#2 says a/b/c", l.get(2).contains("a/b/c"));
// Curtailed: the next line has "... and 2 more"
assertTrue(l.get(3) + "#3 has 'and 2 more'", l.get(3).contains("and 2 more"));
}
@Test
public void TestBare() {
UnicodeKnownIssues uki = new UnicodeKnownIssues(true);
uki.logKnownIssue("a/b/c", "21756", "Something is working!");
List<String> l = printToList(uki);
assertNotNull("no list", l);
assertEquals("message count Of Three", 3, l.size());
assertTrue(l.get(0) + "#0 says Known Issues", l.get(0).contains("Known Issues"));
assertTrue(l.get(1) + "#1 has atlassian URL", l.get(1).contains("browse/ICU-21756"));
assertTrue(l.get(2) + "#2 says a/b/c", l.get(2).contains("a/b/c"));
}
@Test
public void TestUnknown() {
UnicodeKnownIssues uki = new UnicodeKnownIssues(true);
uki.logKnownIssue("a/b/c", "zzz", "Something is working!");
List<String> l = printToList(uki);
assertNotNull("no list", l);
assertEquals("message count Of Three", 3, l.size());
assertTrue(l.get(0) + "#0 says Known Issues", l.get(0).contains("Known Issues"));
assertTrue(l.get(1) + "#1 has unknown ticket", l.get(1).contains("Unknown Ticket"));
assertTrue(l.get(2) + "#2 says a/b/c", l.get(2).contains("a/b/c"));
}
@Test
public void TestCldrLink() {
UnicodeKnownIssues uki = new UnicodeKnownIssues(true);
uki.logKnownIssue("a/b/c", "CLDR-9787", "Something is working!");
List<String> l = printToList(uki);
assertNotNull("no list", l);
assertEquals("message count Of Three", 3, l.size());
assertTrue(l.get(0) + "#0 says Known Issues", l.get(0).contains("Known Issues"));
assertTrue(l.get(1) + "#1 has atlassian URL", l.get(1).contains("browse/CLDR-9787"));
assertTrue(l.get(2) + "#2 says a/b/c", l.get(2).contains("a/b/c"));
}
@Test
public void TestCldrBug() {
UnicodeKnownIssues uki = new UnicodeKnownIssues(true);
uki.logKnownIssue("a/b/c", "cldrbug:9787", "Something is working!");
List<String> l = printToList(uki);
assertNotNull("no list", l);
assertEquals("message count Of Three", 3, l.size());
assertTrue(l.get(0) + "#0 says Known Issues", l.get(0).contains("Known Issues"));
assertTrue(l.get(1) + "#1 has atlassian URL", l.get(1).contains("browse/CLDR-9787"));
assertTrue(l.get(2) + "#2 says a/b/c", l.get(2).contains("a/b/c"));
}
@Test
public void TestNoProblem() {
UnicodeKnownIssues uki = new UnicodeKnownIssues(true);
List<String> l = printToList(uki);
assertNotNull("no list", l);
assertEquals("message count Of Zero", 0, l.size());
}
List<String> printToList(UnicodeKnownIssues uki) {
// TODO: for JDK 1.8
// final List<String> l = new LinkedList<>();
// uki.printKnownIssues(s -> l.add(s));
// TODO: Pre JDK 1.8 below
MyConsumer m = new MyConsumer();
uki.printKnownIssues(m);
return m.l;
}
// TODO: remove for JDK 1.8
static final class MyConsumer implements UnicodeKnownIssues.Consumer<String> {
final List<String> l = new LinkedList<>();
@Override
public void accept(String t) {
l.add(t);
}
}
}

View file

@ -22,6 +22,7 @@ import java.util.Random;
import java.util.TreeMap;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
@ -112,6 +113,17 @@ abstract public class TestFmwk extends AbstractTestLog {
}
}
@AfterClass
public final static void testClassTeardown() {
getParams().knownIssues.printKnownIssues(new UnicodeKnownIssues.Consumer<String>() {
// TODO: make this a Lambda once JDK 1.8 ships
public void accept(String t) {
System.out.println(t);
}
});
getParams().knownIssues.reset();
}
private static TestParams getParams() {
//return paramsReference.get();
return testParams;
@ -170,10 +182,6 @@ abstract public class TestFmwk extends AbstractTestLog {
}
static final String ICU_TRAC_URL = "http://bugs.icu-project.org/trac/ticket/";
static final String CLDR_TRAC_URL = "http://unicode.org/cldr/trac/ticket/";
static final String CLDR_TICKET_PREFIX = "cldrbug:";
/**
* Log the known issue.
* This method returns true unless -prop:logKnownIssue=no is specified
@ -189,41 +197,12 @@ abstract public class TestFmwk extends AbstractTestLog {
if (!getBooleanProperty("logKnownIssue", true)) {
return false;
}
// TODO: This method currently does not do very much.
// See http://bugs.icu-project.org/trac/ticket/12589
StringBuffer descBuf = new StringBuffer();
// TODO(junit) : what to do about this?
final String path = "";
//getParams().stack.appendPath(descBuf);
if (comment != null && comment.length() > 0) {
descBuf.append(" (" + comment + ")");
}
String description = descBuf.toString();
String ticketLink = "Unknown Ticket";
if (ticket != null && ticket.length() > 0) {
boolean isCldr = false;
ticket = ticket.toLowerCase(Locale.ENGLISH);
if (ticket.startsWith(CLDR_TICKET_PREFIX)) {
isCldr = true;
ticket = ticket.substring(CLDR_TICKET_PREFIX.length());
}
ticketLink = (isCldr ? CLDR_TRAC_URL : ICU_TRAC_URL) + ticket;
}
if (getParams().knownIssues == null) {
getParams().knownIssues = new TreeMap<String, List<String>>();
}
List<String> lines = getParams().knownIssues.get(ticketLink);
if (lines == null) {
lines = new ArrayList<String>();
getParams().knownIssues.put(ticketLink, lines);
}
if (!lines.contains(description)) {
lines.add(description);
}
getParams().knownIssues.logKnownIssue(path, ticket, comment);
return true;
}
@ -365,7 +344,7 @@ abstract public class TestFmwk extends AbstractTestLog {
private SecurityManager testSecurityManager;
private SecurityManager originalSecurityManager;
private Map<String, List<String>> knownIssues;
private UnicodeKnownIssues knownIssues = null;
private Properties props;
@ -377,6 +356,7 @@ abstract public class TestFmwk extends AbstractTestLog {
TestParams params = new TestParams();
Properties props = System.getProperties();
params.parseProperties(props);
params.knownIssues = new UnicodeKnownIssues(params.getBooleanProperty("allKnownIssues", false));
return params;
}

View file

@ -0,0 +1,154 @@
// © 2021 and later: Unicode, Inc. and others.
// License & terms of use: https://www.unicode.org/copyright.html
package com.ibm.icu.dev.test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Known issues manager.
* Intended to be shared between ICU, CLDR, &c.
* Test frameworks can create an instance of this to manage known issues
*/
public class UnicodeKnownIssues {
/**
* From Java 1.8
*/
public interface Consumer<T> { void accept(T t); }
private Map<String, List<String>> knownIssues = new TreeMap<>();
/**
* Max number of lines to show by default (including the "more")
* unless -allKnownIssues is given. Must be at least 2.
*/
public static final int KNOWN_ISSUES_CURTAILMENT = 2;
/**
* true if all issues should be shown, false if they should
* be curtailed.
*/
private boolean allKnownIssues;
/**
* Construct a known issue manager
* @param allKnownIssues true if all known issues should be printed,
* not curtailed
*/
public UnicodeKnownIssues(boolean allKnownIssues) {
this.allKnownIssues = allKnownIssues;
}
/**
* Base URL for browsing Unicode JIRA
*/
public static final String UNICODE_JIRA_BROWSE = "https://unicode-org.atlassian.net/browse/";
static final Pattern ICU_TICKET_PATTERN = Pattern.compile(
"(?i)(?:icu-)?(\\d+)"
);
static final Pattern CLDR_TICKET_PATTERN = Pattern.compile(
"(?i)cldr(?:bug:)?(?:-)?(\\d+)"
);
/**
* Match all linkable ticket patterns
* @see {org.unicode.cldr.util.CLDRURLS#CLDR_TICKET_BROWSE}
*/
static final Pattern UNICODE_JIRA_PATTERN = Pattern.compile(
"(CLDR|ICU)-(\\d+)"
);
/**
* Log the known issue.
* Call this from the test framework when logKnownIssue() is called.
*
* @param path Path to the error, will be returned in the
* known issue list
* @param ticket A ticket number string. For an ICU ticket, use "ICU-10245".
* For a CLDR ticket, use "CLDR-12345".
* For compatibility, "1234" -> ICU-1234 and "cldrbug:456" -> CLDR-456
* @param comment Additional comment, or null
*
*/
public void logKnownIssue(String path, String ticket, String comment) {
StringBuilder descBuf = new StringBuilder(path);
if (comment != null && comment.length() > 0) {
descBuf.append(" (" + comment + ")");
}
String description = descBuf.toString();
String ticketLink = "Unknown Ticket";
if (ticket != null && ticket.length() > 0) {
Matcher matcher = ICU_TICKET_PATTERN.matcher(ticket);
if (matcher.matches()) {
ticketLink = "ICU-" + matcher.group(1);
} else {
matcher = CLDR_TICKET_PATTERN.matcher(ticket);
if (matcher.matches()) {
ticketLink = "CLDR-" + matcher.group(1);
}
}
}
List<String> lines = knownIssues.get(ticketLink);
if (lines == null) {
lines = new ArrayList<>();
knownIssues.put(ticketLink, lines);
}
if (!lines.contains(description)) {
lines.add(description);
}
}
/**
* Print out all known issues to the logFn.
* Usage: printKnownIssues(System.out::println)
* @param logFn consumer for Strings (e.g. System.out::println)
* @return true if (!allKnownIssues) and we had to curtail
*/
public boolean printKnownIssues(Consumer<String> logFn) {
boolean didCurtail = false;
if (knownIssues.isEmpty()) {
return false;
}
logFn.accept("\n " + knownIssues.size() + " Known Issues:");
for (Entry<String, List<String>> entry : knownIssues.entrySet()) {
String ticketLink = entry.getKey();
if (UNICODE_JIRA_PATTERN.matcher(ticketLink) != null) {
logFn.accept(ticketLink + " <" + UNICODE_JIRA_BROWSE + ticketLink + ">");
} else {
// Unknown or something else
logFn.accept("<" + ticketLink + ">");
}
List<String> entries = entry.getValue();
int issuesToShow = entries.size();
if (!allKnownIssues && issuesToShow > KNOWN_ISSUES_CURTAILMENT) {
issuesToShow = (KNOWN_ISSUES_CURTAILMENT - 1);
}
for (int i=0; i<issuesToShow; i++) {
logFn.accept(" - " + entries.get(i));
}
if (entries.size() > issuesToShow) {
didCurtail = true;
logFn.accept(" ... and " +
(entries.size() - issuesToShow) + " more");
}
}
return didCurtail;
}
/**
* Reset the known issues
*/
public void reset() {
knownIssues.clear();
}
}