diff --git a/executors/icu4j/74/executor-icu4j/pom.xml b/executors/icu4j/74/executor-icu4j/pom.xml index 3d8050be..3f9a2427 100644 --- a/executors/icu4j/74/executor-icu4j/pom.xml +++ b/executors/icu4j/74/executor-icu4j/pom.xml @@ -19,6 +19,100 @@ jar + + + + icu73 + + + com.ibm.icu + icu4j + 73.2 + + + + + + maven-surefire-plugin + + + + **/collator/icu74/*Test.java + **/langnames/icu74/*Test.java + **/likelysubtags/icu74/*Test.java + **/listformatter/icu74/*Test.java + + **/numberformatter/icu74/*Test.java + **/pluralrules/icu74/*Test.java + **/relativedatetimeformat/icu74/*Test.java + + + + + + + + icu74 + + + com.ibm.icu + icu4j + 74.2 + + + + + + maven-surefire-plugin + + + + **/collator/icu74/*Test.java + **/langnames/icu74/*Test.java + **/likelysubtags/icu74/*Test.java + **/listformatter/icu74/*Test.java + **/messageformat2/icu74/*Test.java + **/numberformatter/icu74/*Test.java + **/pluralrules/icu74/*Test.java + **/relativedatetimeformat/icu74/*Test.java + + + + + + + + icu75 + + + com.ibm.icu + icu4j + 75.1 + + + + + + maven-surefire-plugin + + + + **/collator/icu74/*Test.java + **/langnames/icu74/*Test.java + **/likelysubtags/icu74/*Test.java + **/listformatter/icu74/*Test.java + **/messageformat2/icu75/*Test.java + **/numberformatter/icu74/*Test.java + **/pluralrules/icu74/*Test.java + **/relativedatetimeformat/icu74/*Test.java + + + + + + + + junit @@ -27,12 +121,8 @@ test - - - com.ibm.icu - icu4j - 74.2 - + diff --git a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/Icu4jExecutor.java b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/Icu4jExecutor.java index cc1edb47..3ed36d33 100644 --- a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/Icu4jExecutor.java +++ b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/Icu4jExecutor.java @@ -9,6 +9,7 @@ import org.unicode.conformance.testtype.ITestType; import org.unicode.conformance.testtype.ITestTypeOutputJson; import org.unicode.conformance.testtype.collator.CollatorTester; +import org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterTester; import org.unicode.conformance.testtype.langnames.LangNamesTester; import org.unicode.conformance.testtype.likelysubtags.LikelySubtagsTester; import org.unicode.conformance.testtype.listformatter.ListFormatterTester; @@ -118,6 +119,8 @@ public static String getTestCaseResponse(String inputLine) throws Exception { ITestType testType; if (testTypeStr.equals("collation_short")) { testType = CollatorTester.INSTANCE; + } else if (testTypeStr.equals("datetime_fmt")) { + testType = DateTimeFormatterTester.INSTANCE; } else if (testTypeStr.equals("lang_names")) { testType = LangNamesTester.INSTANCE; } else if (testTypeStr.equals("likely_subtags")) { diff --git a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterDateStyle.java b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterDateStyle.java new file mode 100644 index 00000000..0a78718a --- /dev/null +++ b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterDateStyle.java @@ -0,0 +1,21 @@ +package org.unicode.conformance.testtype.datetimeformatter; + +public enum DateTimeFormatterDateStyle { + FULL, + LONG, + MEDIUM, + SHORT; + + public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterDateStyle DEFAULT = MEDIUM; + + public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterDateStyle getFromString( + String s) { + try { + return org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterDateStyle.valueOf( + s.toUpperCase()); + } catch (Exception e) { + return DEFAULT; + } + } + +} diff --git a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterInputJson.java b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterInputJson.java new file mode 100644 index 00000000..46f9e408 --- /dev/null +++ b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterInputJson.java @@ -0,0 +1,37 @@ +package org.unicode.conformance.testtype.datetimeformatter; + +import java.util.Date; +import com.ibm.icu.util.Calendar; + +import org.unicode.conformance.testtype.ITestTypeInputJson; + +public class DateTimeFormatterInputJson implements ITestTypeInputJson { + + public String testType; + + public String label; + + public String locale; + + // UTC formatted time + public String inputString; + + public Date myDate; + + public String skeleton; + + public DateTimeFormatterDateStyle dateStyle; + + public DateTimeFormatterTimeStyle timeStyle; + + // TODO!!! + public String calendarString; + // Set calendar from calendarString! + public Calendar calendar; + + public String numberingSystem; + + public String timeZone; + + public String timeZoneName; +} diff --git a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterOutputJson.java b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterOutputJson.java new file mode 100644 index 00000000..fd948e10 --- /dev/null +++ b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterOutputJson.java @@ -0,0 +1,16 @@ +package org.unicode.conformance.testtype.datetimeformatter; + +import org.unicode.conformance.testtype.ITestTypeOutputJson; + +public class DateTimeFormatterOutputJson implements ITestTypeOutputJson { + + public String test_type; + + public String label; + + public String result; + + public String error; + + public String error_message; +} diff --git a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterTester.java b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterTester.java new file mode 100644 index 00000000..953403bb --- /dev/null +++ b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterTester.java @@ -0,0 +1,167 @@ +package org.unicode.conformance.testtype.datetimeformatter; + +import java.text.ParseException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +import java.util.Date; + +import com.ibm.icu.util.Calendar; +import com.ibm.icu.text.DateFormat; +import com.ibm.icu.util.ULocale; + +import io.lacuna.bifurcan.IMap; +import io.lacuna.bifurcan.Map; + + +import org.unicode.conformance.ExecutorUtils; +import org.unicode.conformance.testtype.ITestType; +import org.unicode.conformance.testtype.ITestTypeInputJson; +import org.unicode.conformance.testtype.ITestTypeOutputJson; + +public class DateTimeFormatterTester implements ITestType { + + public static DateTimeFormatterTester INSTANCE = new DateTimeFormatterTester(); + + @Override + public ITestTypeInputJson inputMapToJson(Map inputMapData) { + DateTimeFormatterInputJson result = new DateTimeFormatterInputJson(); + + result.label = (String) inputMapData.get("label", null); + result.locale = (String) inputMapData.get("locale", null); + result.skeleton = (String) inputMapData.get("skeleton", null); + + result.inputString = (String) inputMapData.get("input_string", null); + + java.util.Map inputOptions = + (java.util.Map) inputMapData.get("options", null); + + result.timeZone = (String) inputOptions.get("timeZone"); + ZoneId thisZoneId; + if (result.timeZone == null) { + thisZoneId = ZoneId.systemDefault(); + } else { + thisZoneId = ZoneId.of(result.timeZone); + } + + // Extract ISO part of the input string to parse. + String inputStringDateTime = result.inputString.substring(0, 25); + + // For parsing the input string and converting to java.util.date + LocalDateTime parsedLocalDateTime = + LocalDateTime.parse(inputStringDateTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + result.myDate = + java.util.Date.from(parsedLocalDateTime.atZone(thisZoneId) + .toInstant()); + + result.dateStyle = DateTimeFormatterDateStyle.getFromString( + "" + inputOptions.get("dateStyle") + ); + + result.timeStyle = DateTimeFormatterTimeStyle.getFromString( + "" + inputOptions.get("timeStyle") + ); + + result.calendarString = (String) inputOptions.get("calendar"); + + // TODO!!! Get calendar object. Depends on timezone and locale. + // Just a placeholder for now. + result.calendar = Calendar.getInstance(); + + result.numberingSystem = (String) inputOptions.get("numberingSystem"); + + result.timeZoneName = (String) inputOptions.get("timeZoneName"); + + return result; + } + + public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { + DateTimeFormatterInputJson input = (DateTimeFormatterInputJson) inputJson; + + // partially construct output + DateTimeFormatterOutputJson output = (DateTimeFormatterOutputJson) getDefaultOutputJson(); + output.label = input.label; + + try { + output.result = getDateTimeFormatterResultString(input); + } catch (Exception e) { + output.error = e.getMessage(); + output.error_message = e.getMessage(); + return output; + } + + // If we get here, it's a pass/fail result (supported options and no runtime errors/exceptions) + return output; + } + + @Override + public ITestTypeOutputJson getDefaultOutputJson() { + return new DateTimeFormatterOutputJson(); + } + + public IMap convertOutputToMap(ITestTypeOutputJson outputJson) { + DateTimeFormatterOutputJson output = (DateTimeFormatterOutputJson) outputJson; + return new io.lacuna.bifurcan.Map() + .put("label", output.label) + .put("result", output.result); + } + + @Override + public String formatOutputJson(ITestTypeOutputJson outputJson) { + return ExecutorUtils.GSON.toJson((DateTimeFormatterOutputJson) outputJson); + } + + public String getDateTimeFormatterResultString(DateTimeFormatterInputJson input) { + + ULocale locale = ULocale.forLanguageTag(input.locale); + + int dateStyle; + switch (input.dateStyle) { + case FULL: + dateStyle = DateFormat.FULL; + break; + case LONG: + dateStyle = DateFormat.LONG; + break; + default: + case MEDIUM: + dateStyle = DateFormat.MEDIUM; + break; + case SHORT: + dateStyle = DateFormat.SHORT; + break; + } + + int timeStyle; + switch (input.timeStyle) { + case FULL: + timeStyle = DateFormat.FULL; + break; + case LONG: + timeStyle = DateFormat.LONG; + break; + default: + case MEDIUM: + timeStyle = DateFormat.MEDIUM; + break; + case SHORT: + timeStyle = DateFormat.SHORT; + break; + + } + + // Get calendar and timezone as needed. + Calendar cal = input.calendar; + + DateFormat dtf; + if (input.skeleton != null) { + dtf = DateFormat.getInstanceForSkeleton(cal, input.skeleton, locale); + } else { + dtf = DateFormat.getDateTimeInstance(cal, dateStyle, timeStyle, locale); + } + + return dtf.format(input.myDate); + } + +} \ No newline at end of file diff --git a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterTimeStyle.java b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterTimeStyle.java new file mode 100644 index 00000000..50842fe9 --- /dev/null +++ b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterTimeStyle.java @@ -0,0 +1,22 @@ +package org.unicode.conformance.testtype.datetimeformatter; + +public enum DateTimeFormatterTimeStyle { + FULL, + LONG, + MEDIUM, + SHORT; + + public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterTimeStyle DEFAULT = MEDIUM; + + public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterTimeStyle getFromString( + String s) { + try { + return org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterTimeStyle.valueOf( + s.toUpperCase()); + } catch (Exception e) { + return DEFAULT; + } + } + +} + diff --git a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/listformatter/ListFormatterTester.java b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/listformatter/ListFormatterTester.java index d9b953ea..cdc00f0f 100644 --- a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/listformatter/ListFormatterTester.java +++ b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/listformatter/ListFormatterTester.java @@ -26,7 +26,8 @@ public ITestTypeInputJson inputMapToJson(Map inputMapData) { result.label = (String) inputMapData.get("label", null); result.locale = (String) inputMapData.get("locale", null); - java.util.Map inputOptions = (java.util.Map) inputMapData.get("options", null); + java.util.Map inputOptions = (java.util.Map) inputMapData.get( + "options", null); result.listType = ListFormatterType.getFromString( "" + inputOptions.get("list_type") @@ -68,7 +69,7 @@ public ITestTypeOutputJson getDefaultOutputJson() { @Override public IMap convertOutputToMap(ITestTypeOutputJson outputJson) { ListFormatterOutputJson output = (ListFormatterOutputJson) outputJson; - return new io.lacuna.bifurcan.Map() + return new io.lacuna.bifurcan.Map() .put("label", output.label) .put("result", output.result); } @@ -84,27 +85,33 @@ public String getListFormatResultString(ListFormatterInputJson input) { ULocale locale = ULocale.forLanguageTag(input.locale); switch (input.listType) { - case DISJUNCTION: listType = Type.OR; + case DISJUNCTION: + listType = Type.OR; break; - case UNIT: listType = Type.UNITS; + case UNIT: + listType = Type.UNITS; break; default: - case CONJUNCTION: listType = Type.AND; + case CONJUNCTION: + listType = Type.AND; break; } switch (input.style) { - case NARROW: listWidth = Width.NARROW; + case NARROW: + listWidth = Width.NARROW; break; - case SHORT: listWidth = Width.SHORT; + case SHORT: + listWidth = Width.SHORT; break; default: - case LONG: listWidth = Width.WIDE; + case LONG: + listWidth = Width.WIDE; break; } ListFormatter lf = ListFormatter.getInstance(locale, listType, listWidth); - return lf.format(input.inputList); + return lf.format(input.inputList); } } diff --git a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/relativedatetimeformat/RelativeDateTimeFormatTester.java b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/relativedatetimeformat/RelativeDateTimeFormatTester.java index 2ea01d4a..426ee5c9 100644 --- a/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/relativedatetimeformat/RelativeDateTimeFormatTester.java +++ b/executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/relativedatetimeformat/RelativeDateTimeFormatTester.java @@ -1,124 +1,136 @@ - package org.unicode.conformance.testtype.relativedatetimeformat; +package org.unicode.conformance.testtype.relativedatetimeformat; - import com.ibm.icu.text.DisplayContext; - import com.ibm.icu.text.NumberFormat; - import com.ibm.icu.text.RelativeDateTimeFormatter; - import com.ibm.icu.text.RelativeDateTimeFormatter.Style; - import com.ibm.icu.util.ULocale; +import com.ibm.icu.text.DisplayContext; +import com.ibm.icu.text.NumberFormat; +import com.ibm.icu.text.RelativeDateTimeFormatter; +import com.ibm.icu.text.RelativeDateTimeFormatter.Style; +import com.ibm.icu.util.ULocale; - import io.lacuna.bifurcan.IMap; - import io.lacuna.bifurcan.Map; +import io.lacuna.bifurcan.IMap; +import io.lacuna.bifurcan.Map; - import org.unicode.conformance.ExecutorUtils; - import org.unicode.conformance.testtype.ITestType; - import org.unicode.conformance.testtype.ITestTypeInputJson; - import org.unicode.conformance.testtype.ITestTypeOutputJson; +import org.unicode.conformance.ExecutorUtils; +import org.unicode.conformance.testtype.ITestType; +import org.unicode.conformance.testtype.ITestTypeInputJson; +import org.unicode.conformance.testtype.ITestTypeOutputJson; - public class RelativeDateTimeFormatTester implements ITestType { +public class RelativeDateTimeFormatTester implements ITestType { - public static RelativeDateTimeFormatTester INSTANCE = new RelativeDateTimeFormatTester(); + public static RelativeDateTimeFormatTester INSTANCE = new RelativeDateTimeFormatTester(); - @Override - public ITestTypeInputJson inputMapToJson(Map inputMapData) { - RelativeDateTimeFormatInputJson result = new RelativeDateTimeFormatInputJson(); + @Override + public ITestTypeInputJson inputMapToJson(Map inputMapData) { + RelativeDateTimeFormatInputJson result = new RelativeDateTimeFormatInputJson(); - result.label = (String) inputMapData.get("label", null); - result.locale = (String) inputMapData.get("locale", null); - result.count = (String) inputMapData.get("count", "0"); - result.quantity = Double.parseDouble(result.count); + result.label = (String) inputMapData.get("label", null); + result.locale = (String) inputMapData.get("locale", null); + result.count = (String) inputMapData.get("count", "0"); + result.quantity = Double.parseDouble(result.count); - result.numberingSystem = (String) inputMapData.get("numbering_system", null); + result.numberingSystem = (String) inputMapData.get("numbering_system", null); - java.util.Map inputOptions = - (java.util.Map) inputMapData.get("options", null); + java.util.Map inputOptions = + (java.util.Map) inputMapData.get("options", null); - result.style = RelativeDateTimeFormatStyle.getFromString( - "" + inputOptions.get("style") - ); + result.style = RelativeDateTimeFormatStyle.getFromString( + "" + inputOptions.get("style") + ); - String unitInput = (String) inputMapData.get("unit", "0"); - result.unit = RelativeDateTimeFormatUnits.getFromString( - unitInput); + String unitInput = (String) inputMapData.get("unit", "0"); + result.unit = RelativeDateTimeFormatUnits.getFromString( + unitInput); - return result; - } - - @Override - public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { - RelativeDateTimeFormatInputJson input = (RelativeDateTimeFormatInputJson) inputJson; + return result; + } - // partially construct output - RelativeDateTimeFormatOutputJson output = (RelativeDateTimeFormatOutputJson) getDefaultOutputJson(); - output.label = input.label; + @Override + public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { + RelativeDateTimeFormatInputJson input = (RelativeDateTimeFormatInputJson) inputJson; - try { - output.result = getRelativeDateTimeFormatResultString(input); - } catch (Exception e) { - output.error = e.getMessage(); - output.error_message = e.getMessage(); - return output; - } + // partially construct output + RelativeDateTimeFormatOutputJson output = (RelativeDateTimeFormatOutputJson) getDefaultOutputJson(); + output.label = input.label; - // If we get here, it's a pass/fail result (supported options and no runtime errors/exceptions) + try { + output.result = getRelativeDateTimeFormatResultString(input); + } catch (Exception e) { + output.error = e.getMessage(); + output.error_message = e.getMessage(); return output; } - @Override - public ITestTypeOutputJson getDefaultOutputJson() { - return new RelativeDateTimeFormatOutputJson(); - } + // If we get here, it's a pass/fail result (supported options and no runtime errors/exceptions) + return output; + } - public IMap convertOutputToMap(ITestTypeOutputJson outputJson) { - RelativeDateTimeFormatOutputJson output = (RelativeDateTimeFormatOutputJson) outputJson; - return new io.lacuna.bifurcan.Map() - .put("label", output.label) - .put("result", output.result); - } + @Override + public ITestTypeOutputJson getDefaultOutputJson() { + return new RelativeDateTimeFormatOutputJson(); + } - public String formatOutputJson(ITestTypeOutputJson outputJson) { - return ExecutorUtils.GSON.toJson(outputJson); + public IMap convertOutputToMap(ITestTypeOutputJson outputJson) { + RelativeDateTimeFormatOutputJson output = (RelativeDateTimeFormatOutputJson) outputJson; + return new io.lacuna.bifurcan.Map() + .put("label", output.label) + .put("result", output.result); + } + + public String formatOutputJson(ITestTypeOutputJson outputJson) { + return ExecutorUtils.GSON.toJson(outputJson); + } + + public String getRelativeDateTimeFormatResultString(RelativeDateTimeFormatInputJson input) { + ULocale locale = ULocale.forLanguageTag(input.locale); + Style style; + RelativeDateTimeFormatter.RelativeDateTimeUnit unit; + + switch (input.unit) { + default: + case DAY: + unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.DAY; + break; + case HOUR: + unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.HOUR; + break; + case MINUTE: + unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.MINUTE; + break; + case MONTH: + unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.MONTH; + break; + case QUARTER: + unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.QUARTER; + break; + case SECOND: + unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.SECOND; + break; + case WEEK: + unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.WEEK; + break; + case YEAR: + unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.YEAR; + break; } - public String getRelativeDateTimeFormatResultString(RelativeDateTimeFormatInputJson input) { - ULocale locale = ULocale.forLanguageTag(input.locale); - Style style; - RelativeDateTimeFormatter.RelativeDateTimeUnit unit; - - switch (input.unit) { - default: - case DAY: unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.DAY; - break; - case HOUR: unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.HOUR; - break; - case MINUTE: unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.MINUTE; - break; - case MONTH: unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.MONTH; - break; - case QUARTER: unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.QUARTER; - break; - case SECOND: unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.SECOND; - break; - case WEEK: unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.WEEK; - break; - case YEAR: unit = RelativeDateTimeFormatter.RelativeDateTimeUnit.YEAR; - break; - } - - switch (input.style) { - case NARROW: style = RelativeDateTimeFormatter.Style.NARROW; - break; - case SHORT: style = RelativeDateTimeFormatter.Style.SHORT; - break; - default: - case LONG: style = RelativeDateTimeFormatter.Style.LONG; - break; - } - - NumberFormat nf = null; - DisplayContext dc = DisplayContext.CAPITALIZATION_NONE; - - RelativeDateTimeFormatter rdtf = - RelativeDateTimeFormatter.getInstance(locale, nf, style, dc); - - return rdtf.format(input.quantity, unit); + + switch (input.style) { + case NARROW: + style = RelativeDateTimeFormatter.Style.NARROW; + break; + case SHORT: + style = RelativeDateTimeFormatter.Style.SHORT; + break; + default: + case LONG: + style = RelativeDateTimeFormatter.Style.LONG; + break; } + + NumberFormat nf = null; + DisplayContext dc = DisplayContext.CAPITALIZATION_NONE; + + RelativeDateTimeFormatter rdtf = + RelativeDateTimeFormatter.getInstance(locale, nf, style, dc); + + return rdtf.format(input.quantity, unit); } +} diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/collator/CollatorTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/collator/icu74/CollatorTest.java similarity index 95% rename from executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/collator/CollatorTest.java rename to executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/collator/icu74/CollatorTest.java index 62af0a33..0115ca86 100644 --- a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/collator/CollatorTest.java +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/collator/icu74/CollatorTest.java @@ -1,4 +1,4 @@ -package org.unicode.conformance.collator; +package org.unicode.conformance.collator.icu74; import static org.junit.Assert.assertTrue; diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/datetimeformatter/DateTimeFormatterTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/datetimeformatter/DateTimeFormatterTest.java new file mode 100644 index 00000000..07f12318 --- /dev/null +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/datetimeformatter/DateTimeFormatterTest.java @@ -0,0 +1,73 @@ +package org.unicode.conformance.datetimeformatter; + +import org.junit.Ignore; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterOutputJson; +import org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterTester; + +public class DateTimeFormatterTest { + + @Test + public void TestDateTime49() { + String testInput = + "{\"test_type\": \"datetime_fmt\", \"input_string\":\"2024-03-07T00:00:01-08:00[America/Los_Angeles][u-ca=gregory]\",\"skeleton\":\"j\",\"locale\":\"en-US\",\"options\":{\"hour\":\"numeric\",\"calendar\":\"gregory\",\"timeZone\":\"America/Los_Angeles\",\"numberingSystem\":\"latn\"},\"hexhash\":\"30c5191c8041eb6d8afa05aab80f811753bc082f\",\"label\":\"49\"}"; + + DateTimeFormatterOutputJson output = + (DateTimeFormatterOutputJson) DateTimeFormatterTester.INSTANCE.getStructuredOutputFromInputStr( + testInput); + assertEquals("12 AM", output.result); + } + + @Test + public void TestDateTime15455() { + String testInput = + "{\"test_type\": \"datetime_fmt\", \"input_string\":\"2001-09-09T01:46:40-07:00[America/Los_Angeles]\",\"skeleton\":\"vvvv\",\"locale\":\"zu\",\"options\":{\"timeZoneName\":\"longGeneric\",\"calendar\":\"persian\",\"timeZone\":\"America/Los_Angeles\",\"numberingSystem\":\"latn\"},\"hexhash\":\"d4cfde2db66f8d4aec9254fc66ef2db298d7a0ba\",\"label\":\"15455\"}"; + + DateTimeFormatterOutputJson output = + (DateTimeFormatterOutputJson) DateTimeFormatterTester.INSTANCE.getStructuredOutputFromInputStr( + testInput); + assertEquals("Isikhathi sase-North American Pacific", output.result); + } + + @Ignore + @Test + public void testDateTime0() { + String testInput = + "{\"test_type\":\"datetime_fmt\", \"input_string\":\"2024-03-07T00:00:01+00:00[UTC][u-ca=gregory]\",\"locale\":\"en-US\",\"options\":{\"dateStyle\":\"short\",\"timeStyle\":\"short\",\"calendar\":\"gregory\",\"timeZone\":\"UTC\",\"numberingSystem\":\"latn\"},\"hexhash\":\"048d17f248ef4f6835d6b9b3bcbfdc934f3fcad5\",\"label\":\"0\"}"; + + DateTimeFormatterOutputJson output = + (DateTimeFormatterOutputJson) DateTimeFormatterTester.INSTANCE.getStructuredOutputFromInputStr( + testInput); + + assertEquals("3/7/24, 12:00 AM", output.result); + } + + @Ignore + @Test + public void testDateTime3864() { + String testInput = + "{\"test_type\":\"datetime_fmt\",\"input_string\":\"2024-03-07T00:00:01+00:00[UTC][u-ca=gregory]\",\"locale\":\"zh-TW\",\"options\":{\"dateStyle\":\"short\",\"timeStyle\":\"short\",\"calendar\":\"gregory\",\"timeZone\":\"UTC\",\"numberingSystem\":\"latn\"},\"hexhash\":\"2f22cb2c0656fd092d12c17b2545cec4ca9f23b3\",\"label\":\"3864\"}"; + + DateTimeFormatterOutputJson output = + (DateTimeFormatterOutputJson) DateTimeFormatterTester.INSTANCE.getStructuredOutputFromInputStr( + testInput); + + assertEquals("2024/3/7 凌晨12:00", output.result); + } + + @Ignore + @Test + public void testDateTime17387() { + + String testInput = "\t{\"test_type\":\"datetime_fmt\",\"input_string\":\"2001-09-09T01:46:40+10:00[Australia/Brisbane]\",\"skeleton\":\"vvvv\",\"locale\":\"und\",\"options\":{\"timeZoneName\":\"longGeneric\",\"calendar\":\"persian\",\"timeZone\":\"Australia/Brisbane\",\"numberingSystem\":\"latn\"},\"hexhash\":\"8a39dcac98f0487ead82dedd3447bdf393b35081\",\"label\":\"17387\"}"; + + DateTimeFormatterOutputJson output = + (DateTimeFormatterOutputJson) DateTimeFormatterTester.INSTANCE.getStructuredOutputFromInputStr( + testInput); + + assertEquals("3/7/24, 12:00 AM", output.result); + } + +} \ No newline at end of file diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/langnames/LangNamesTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/langnames/icu74/LangNamesTest.java similarity index 92% rename from executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/langnames/LangNamesTest.java rename to executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/langnames/icu74/LangNamesTest.java index 80b34e3a..6cfa35b4 100644 --- a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/langnames/LangNamesTest.java +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/langnames/icu74/LangNamesTest.java @@ -1,4 +1,4 @@ -package org.unicode.conformance.langnames; +package org.unicode.conformance.langnames.icu74; import static org.junit.Assert.assertEquals; diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/likelysubtags/LikelySubtagsTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/likelysubtags/icu74/LikelySubtagsTest.java similarity index 95% rename from executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/likelysubtags/LikelySubtagsTest.java rename to executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/likelysubtags/icu74/LikelySubtagsTest.java index 7956470c..a3e3fd63 100644 --- a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/likelysubtags/LikelySubtagsTest.java +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/likelysubtags/icu74/LikelySubtagsTest.java @@ -1,4 +1,4 @@ -package org.unicode.conformance.likelysubtags; +package org.unicode.conformance.likelysubtags.icu74; import static org.junit.Assert.assertEquals; diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/listformatter/ListFormatterTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/listformatter/icu74/ListFormatterTest.java similarity index 96% rename from executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/listformatter/ListFormatterTest.java rename to executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/listformatter/icu74/ListFormatterTest.java index a9fb512b..cbda3962 100644 --- a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/listformatter/ListFormatterTest.java +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/listformatter/icu74/ListFormatterTest.java @@ -1,4 +1,4 @@ -package org.unicode.conformance.listformatter; +package org.unicode.conformance.listformatter.icu74; import static org.junit.Assert.assertEquals; diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/MessageFormatterTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/icu74/MessageFormatterTest.java similarity index 94% rename from executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/MessageFormatterTest.java rename to executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/icu74/MessageFormatterTest.java index 426b7f4b..f936bb46 100644 --- a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/MessageFormatterTest.java +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/icu74/MessageFormatterTest.java @@ -1,4 +1,4 @@ -package org.unicode.conformance.messageformat2; +package org.unicode.conformance.messageformat2.icu74; import static org.junit.Assert.assertEquals; @@ -120,9 +120,6 @@ public void testGetFormattedMessage() { } - // ICU 75 impl output differs from MF2 spec defined at same time point (CLDR 45) - // in what to return in message for non-provided args / formatting errors - @Ignore @Test public void testGetFormattedMessage_usingNonProvidedArg() { // Setup @@ -138,11 +135,11 @@ public void testGetFormattedMessage_usingNonProvidedArg() { String formattedString = MessageFormatTester.INSTANCE.getFormattedMessage(inputJson); // Expect & assert test - String expected = "{:date}"; + String expected = ":date"; assertEquals(expected, formattedString); } - // ICU 75 impl output differs from MF2 spec defined at same time point (CLDR 45) + // TODO: figure out expected output, and then reenable? @Ignore @Test public void testGetFormattedMessage_numberLiteralOperand() { @@ -150,7 +147,7 @@ public void testGetFormattedMessage_numberLiteralOperand() { MessageFormatInputJson inputJson = new MessageFormatInputJson(); inputJson.label = "00035"; inputJson.locale = "en-US"; - inputJson.src = "hello {4.2 :integer}"; + inputJson.src = "{hello {|4.2| :integer}}"; inputJson.test_description = "Test of formatting a pattern using an input arg that isn't provided"; List inputs = new ArrayList<>(); inputJson.params = inputs; diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/icu75/MessageFormatterTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/icu75/MessageFormatterTest.java new file mode 100644 index 00000000..b52ba8f4 --- /dev/null +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/messageformat2/icu75/MessageFormatterTest.java @@ -0,0 +1,163 @@ +package org.unicode.conformance.messageformat2.icu75; + +import static org.junit.Assert.assertEquals; + +import com.ibm.icu.util.ULocale; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.unicode.conformance.testtype.messageformat2.IMFInputParam; +import org.unicode.conformance.testtype.messageformat2.MFInputParamDatetime; +import org.unicode.conformance.testtype.messageformat2.MFInputParamObject; +import org.unicode.conformance.testtype.messageformat2.MessageFormatInputJson; +import org.unicode.conformance.testtype.messageformat2.MessageFormatTester; + +public class MessageFormatterTest { + + /** + * The default locale used for all of our tests. Used in @Before + */ + protected final static Locale defaultLocale = Locale.US; + + /** + * The default time zone for all of our tests. Used in @Before + */ + protected final static TimeZone defaultTimeZone = TimeZone.getTimeZone("America/Los_Angeles"); + + private com.ibm.icu.util.TimeZone testStartDefaultIcuTz; + + private TimeZone testStartDefaultJdkTz; + + private ULocale testStartDefaultULocale; + + private Locale testStartDefaultLocale; + + @Rule + public TestName name = new TestName(); + + // Copying test setup behavior from ICU4J CoreTestFmwk / TestFmwk, which + // ensures we pin the default locale and TZ during the test. ICU Formatters + // implicitly use the system's default locale and TZ. + @Before + public final void setup() { + // Just like TestFmwk initializes JDK TimeZone and Locale before every test, + // do the same for ICU TimeZone and ULocale. + ULocale.setDefault(ULocale.forLocale(defaultLocale)); + com.ibm.icu.util.TimeZone.setDefault( + com.ibm.icu.util.TimeZone.getTimeZone(defaultTimeZone.getID())); + + // Save starting timezones + testStartDefaultIcuTz = com.ibm.icu.util.TimeZone.getDefault(); + testStartDefaultJdkTz = TimeZone.getDefault(); + + // Save starting locales + testStartDefaultULocale = ULocale.getDefault(); + testStartDefaultLocale = Locale.getDefault(); + } + + // Copying test teardown beahvior from ICU4J CoreTestFmwk, corresponding to + // the setup work. + @After + public final void teardown() { + String testMethodName = name.getMethodName(); + + // Assert that timezones are in a good state + + com.ibm.icu.util.TimeZone testEndDefaultIcuTz = com.ibm.icu.util.TimeZone.getDefault(); + TimeZone testEndDefaultJdkTz = TimeZone.getDefault(); + + assertEquals("In [" + testMethodName + "] Test should keep in sync ICU & JDK TZs", + testEndDefaultIcuTz.getID(), + testEndDefaultJdkTz.getID()); + + assertEquals("In [" + testMethodName + "] Test should reset ICU default TZ", + testStartDefaultIcuTz.getID(), testEndDefaultIcuTz.getID()); + assertEquals("In [" + testMethodName + "] Test should reset JDK default TZ", + testStartDefaultJdkTz.getID(), testEndDefaultJdkTz.getID()); + + // Assert that locales are in a good state + + ULocale testEndDefaultULocale = ULocale.getDefault(); + Locale testEndDefaultLocale = Locale.getDefault(); + + assertEquals("In [" + testMethodName + "] Test should reset ICU ULocale", + testStartDefaultULocale.toLanguageTag(), testEndDefaultULocale.toLanguageTag()); + assertEquals("In [" + testMethodName + "] Test should reset JDK Locale", + testStartDefaultLocale.toLanguageTag(), testEndDefaultLocale.toLanguageTag()); + } + + @Test + public void testGetFormattedMessage() { + // Setup + MessageFormatInputJson inputJson = new MessageFormatInputJson(); + inputJson.label = "00001"; + inputJson.locale = "en-GB"; + inputJson.src = "Hello {$name}, your card expires on {$exp :datetime skeleton=yMMMdE}!"; + inputJson.test_description = "Test using the ICU4J API doc example for the MessageFormatter class"; + List inputs = new ArrayList<>(); + MFInputParamObject nameArg = new MFInputParamObject(); + nameArg.name = "name"; + nameArg.value = "John"; + inputs.add(nameArg); + MFInputParamDatetime expArg = new MFInputParamDatetime("exp", "2023-03-27T19:42:51"); // March 27, 2023, 7:42:51 PM + expArg.name = "exp"; + inputs.add(expArg); + inputJson.params = inputs; + + // Actual + String formattedString = MessageFormatTester.INSTANCE.getFormattedMessage(inputJson); + + // Expect & assert test + String expected = "Hello John, your card expires on 27/03/2023, 12:42!"; + assertEquals(expected, formattedString); + + } + + // ICU 75 impl output differs from MF2 spec defined at same time point (CLDR 45) + // in what to return in message for non-provided args / formatting errors + @Test + public void testGetFormattedMessage_usingNonProvidedArg() { + // Setup + MessageFormatInputJson inputJson = new MessageFormatInputJson(); + inputJson.label = "00020"; + inputJson.locale = "en-US"; + inputJson.src = ":date"; + inputJson.test_description = "Test of formatting a pattern using an input arg that isn't provided"; + List inputs = new ArrayList<>(); + inputJson.params = inputs; + + // Actual + String formattedString = MessageFormatTester.INSTANCE.getFormattedMessage(inputJson); + + // Expect & assert test + String expected = ":date"; + assertEquals(expected, formattedString); + } + + @Test + public void testGetFormattedMessage_numberLiteralOperand() { + // Setup + MessageFormatInputJson inputJson = new MessageFormatInputJson(); + inputJson.label = "00035"; + inputJson.locale = "en-US"; + inputJson.src = "hello {4.2 :integer}"; + inputJson.test_description = "Test of formatting a pattern using an input arg that isn't provided"; + List inputs = new ArrayList<>(); + inputJson.params = inputs; + + // Actual + String formattedString = MessageFormatTester.INSTANCE.getFormattedMessage(inputJson); + + // Expect & assert test + String expected = "hello 4"; + assertEquals(expected, formattedString); + } + +} diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/NumberFormatterTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/icu74/NumberFormatterTest.java similarity index 97% rename from executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/NumberFormatterTest.java rename to executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/icu74/NumberFormatterTest.java index a3ae7a50..271e8dcd 100644 --- a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/NumberFormatterTest.java +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/icu74/NumberFormatterTest.java @@ -1,4 +1,4 @@ -package org.unicode.conformance.numberformatter; +package org.unicode.conformance.numberformatter.icu74; import static org.junit.Assert.assertEquals; diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/pluralrules/PluralRulesTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/pluralrules/icu74/PluralRulesTest.java similarity index 98% rename from executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/pluralrules/PluralRulesTest.java rename to executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/pluralrules/icu74/PluralRulesTest.java index 5cb2745e..3635d41a 100644 --- a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/pluralrules/PluralRulesTest.java +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/pluralrules/icu74/PluralRulesTest.java @@ -1,4 +1,4 @@ -package org.unicode.conformance.pluralrules; +package org.unicode.conformance.pluralrules.icu74; import static org.junit.Assert.assertEquals; diff --git a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/relativedatetimeformat/RelativeDateTimeFormatTest.java b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/relativedatetimeformat/icu74/RelativeDateTimeFormatTest.java similarity index 98% rename from executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/relativedatetimeformat/RelativeDateTimeFormatTest.java rename to executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/relativedatetimeformat/icu74/RelativeDateTimeFormatTest.java index 74553f7e..2e6ac272 100644 --- a/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/relativedatetimeformat/RelativeDateTimeFormatTest.java +++ b/executors/icu4j/74/executor-icu4j/src/test/java/org/unicode/conformance/relativedatetimeformat/icu74/RelativeDateTimeFormatTest.java @@ -1,4 +1,4 @@ -package org.unicode.conformance.relativedatetimeformat; +package org.unicode.conformance.relativedatetimeformat.icu74; import static org.junit.Assert.assertEquals; diff --git a/run_config.json b/run_config.json index f0492b28..e86e3ee8 100644 --- a/run_config.json +++ b/run_config.json @@ -306,17 +306,62 @@ "per_execution": 10000 } }, + { + "prereq": { + "name": "mvn-icu4j-73-shaded", + "version": "73", + "command": "mvn -q -P icu73 -f ../executors/icu4j/74/executor-icu4j/pom.xml package" + }, + "run": { + "icu_version": "icu73", + "exec": "icu4j", + "test_type": [ + "collation_short", + "datetime_fmt", + "lang_names", + "likely_subtags", + "list_fmt", + "number_fmt", + "plural_rules", + "rdt_fmt" + ], + "per_execution": 10000 + } + }, { "prereq": { "name": "mvn-icu4j-74-shaded", "version": "74", - "command": "mvn -q -f ../executors/icu4j/74/executor-icu4j/pom.xml package" + "command": "mvn -q -P icu74 -f ../executors/icu4j/74/executor-icu4j/pom.xml package" }, "run": { "icu_version": "icu74", "exec": "icu4j", "test_type": [ "collation_short", + "datetime_fmt", + "lang_names", + "likely_subtags", + "list_fmt", + "number_fmt", + "plural_rules", + "rdt_fmt" + ], + "per_execution": 10000 + } + }, + { + "prereq": { + "name": "mvn-icu4j-75-shaded", + "version": "75", + "command": "mvn -q -P icu75 -f ../executors/icu4j/74/executor-icu4j/pom.xml package" + }, + "run": { + "icu_version": "icu75", + "exec": "icu4j", + "test_type": [ + "collation_short", + "datetime_fmt", "lang_names", "likely_subtags", "list_fmt", @@ -330,8 +375,8 @@ { "prereq": { "name": "mvn-icu4j-74-shaded", - "version": "74", - "command": "mvn -q -f ../executors/icu4j/74/executor-icu4j/pom.xml package" + "version": "75", + "command": "mvn -q -P icu75 -f ../executors/icu4j/74/executor-icu4j/pom.xml package -DskipTests=true" }, "run": { "icu_version": "icu75", 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/datasets.py b/testdriver/datasets.py index c8a59151..eb102729 100644 --- a/testdriver/datasets.py +++ b/testdriver/datasets.py @@ -206,7 +206,7 @@ class ExecutorLang(Enum): "dart_native" : "../executors/dart_native/bin/executor/executor.exe", "rust" : "../executors/rust/target/release/executor", "cpp": "LD_LIBRARY_PATH=/tmp/icu/icu/usr/local/lib ../executors/cpp/executor", - "icu4j" : "mvn -q -f ../executors/icu4j/74/executor-icu4j/pom.xml compile exec:java -Dexec.mainClass=org.unicode.conformance.Icu4jExecutor" + "icu4j" : "java -jar ../executors/icu4j/74/executor-icu4j/target/executor-icu4j-1.0-SNAPSHOT-shaded.jar" } class ParallelMode(Enum): 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 = ''; @@ -377,7 +383,7 @@ } else { output = item[key]; } - html.push(''); + html.push(''); } } 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 @@

$platform_info

$total_tests attempted. Pass: $passing_tests, Fail: $failing_tests, Errors: $error_count, - Unsupported: $unsupported, + Unsupported: $unsupported

, Known issues: $unsupported

@@ -717,7 +723,7 @@

Acknowledgements

Failing tests ($failing_tests) -

+

@@ -741,6 +747,32 @@

Acknowledgements

+
+ Known issues ($known_issue_count) +

+
+
+
+
+ Known issues characterized +
+
+

Filtered count = 0 + + +

+
+
+
+
+
+
+
+
+
+
+
+
Test errors ($error_count)

Test Errors ($error_count)

@@ -793,30 +825,6 @@

Test Errors ($error_count)

-
- Known issues ($known_issue_count) -
-
-
-
- Known issues characterized -
-
-

Filtered count = 0 - - -

-
-
-
-
-
-
-
-
-
-
-
diff --git a/verifier/testreport.py b/verifier/testreport.py index a8071e37..1a79eb56 100644 --- a/verifier/testreport.py +++ b/verifier/testreport.py @@ -3,6 +3,9 @@ # TODO: get templates from this module instead of local class from report_template import reportTemplate +# For identifying test cases that are known problems +from check_known_issues import check_issues + from collections import defaultdict from difflib import HtmlDiff @@ -111,6 +114,7 @@ def __init__(self, report_path, report_html_path): self.passing_tests = [] self.test_errors = [] self.unsupported_cases = [] + self.known_issues = [] self.templates = templates = reportTemplate() @@ -161,7 +165,7 @@ def __init__(self, report_path, report_html_path): self.known_issue_data = result_class_data( 'known_issues', None, - None, # ?? templates.known_issue_table_template, + self.known_issue_summary_template, templates.known_issue_table_template, self.compute_known_issue_category_summary) @@ -196,6 +200,9 @@ def record_test_error(self, test): def record_unsupported(self, test): self.unsupported_cases.append(test) + def record_known_issue(self, test): + self.known_issues.append(test) + def record_missing_verify_data(self, test): self.missing_verify_data.append(test) @@ -208,21 +215,21 @@ def compute_test_error_summary(self, test_errors, group_tag): for error in test_errors: try: label = error['label'] - details = error.get('error_detail') + details = error.get('error_detail', None) if not details: # Try getting the group_tag - details = error.get(group_tag) + details = error.get(group_tag, None) if isinstance(details, str): detail = details group = group_tag else: - detail = details.get(group_tag) + detail = details.get(group_tag, None) group = group_tag if group: - if not groups.get(group): + if not groups.get(group, None): groups[group] = {detail: []} # insert empty list - if not groups[group].get(detail): + if not groups[group].get(detail, None): groups[group][detail] = [label] else: groups[group][detail].append(label) @@ -235,19 +242,19 @@ def compute_unsupported_category_summary(self, unsupported_cases, group_tag): # For the items, count messages and arguments for each groups = {} for case in unsupported_cases: - error_detail = case.get('error_detail') + error_detail = case.get('error_detail', None) label = case['label'] if isinstance(error_detail, str): detail = error_detail else: if isinstance(error_detail, dict): - detail = error_detail.get(group_tag) + detail = error_detail.get(group_tag, None) else: detail = error_detail # Specific for unsupported options - count instances of the detail value = str(detail) - if groups.get(value): + if groups.get(value, None): groups[value].append(label) else: groups[value] = [label] @@ -278,16 +285,22 @@ def create_report(self): report['platform'] = self.platform_info report['test_environment'] = self.test_environment report['timestamp'] = self.timestamp - report['failCount'] = len(self.failing_tests) + report['passCount'] = len(self.passing_tests) report['failingTests'] = self.failing_tests - report['unsupportedTests'] = len(self.unsupported_cases) - report['missing_verify_data'] = self.missing_verify_data - report['test_error_count'] = len(self.test_errors) + report['failCount'] = len(self.failing_tests) report['test_errors'] = self.test_errors + report['test_error_count'] = len(self.test_errors) + report['unsupported'] = self.unsupported_cases + report['unsupportedTests'] = len(self.unsupported_cases) + report['known_issues'] = self.known_issues + report['known_issue_count'] = len(self.known_issues) + + report['missing_verify_data'] = self.missing_verify_data + self.report = report return json.dumps(report) @@ -361,6 +374,17 @@ def combine_same_sets_of_labels(self, label_sets): return combined_dictionary + + def add_known_issues(self): + # Call functions to identify known issues, moving things from fail, error, and unsupported + # to known_issues as needed + new_known_issues = check_issues( + self.test_type, + [self.failing_tests, self.test_errors, self.unsupported_cases]) + + if new_known_issues: + self.known_issues.extend(new_known_issues) + def create_html_report(self): # Human-readable summary of test results if self.platform_info['icuVersion'] == 'unknown': @@ -387,8 +411,8 @@ def create_html_report(self): 'failing_tests': len(self.failing_tests), 'error_count': len(self.test_errors), 'unsupported_count': len(self.unsupported_cases), - 'known_issue_count': len(self.known_issues) - + 'known_issue_count': len(self.known_issues), + 'known_issues': self.known_issues # ... } @@ -405,31 +429,38 @@ def create_html_report(self): line = self.fail_line_template.safe_substitute(fail) fail_lines.append(line) - #html_map['failure_table_lines'] = '\n'.join(fail_lines) + # Call functions to identify known issues, moving things from fail, error, and unsupported + # to known_issues as needed + new_known_issues = check_issues( + self.test_type, + [self.failing_tests, self.test_errors, self.unsupported_cases]) + + if new_known_issues: + self.known_issues.extend(new_known_issues) + # Characterize successes, too. - pass_characterized = self.characterize_failures_by_options(self.passing_tests) + pass_characterized = self.characterize_results_by_options(self.passing_tests, 'pass') flat_combined_passing = self.flatten_and_combine(pass_characterized, None) self.save_characterized_file(flat_combined_passing, "pass") # Get and save failures, errors, unsupported - error_characterized = self.characterize_failures_by_options(self.test_errors) + error_characterized = self.characterize_results_by_options(self.test_errors, 'error') flat_combined_errors = self.flatten_and_combine(error_characterized, None) self.save_characterized_file(flat_combined_errors, "error") - unsupported_characterized = self.characterize_failures_by_options(self.unsupported_cases) + unsupported_characterized = self.characterize_results_by_options(self.unsupported_cases, 'unsupported') flat_combined_unsupported = self.flatten_and_combine(unsupported_characterized, None) self.save_characterized_file(flat_combined_unsupported, "unsupported") - - known_issues_characterized = self.characterize_failures_by_options(self.known_issues) + known_issues_characterized = self.characterize_results_by_options(self.known_issues, 'known_issue') flat_combined_known_issues = self.flatten_and_combine(known_issues_characterized, None) self.save_characterized_file(flat_combined_known_issues, "known_issues") # TODO: Should we compute top 3-5 overlaps for each set? # Flatten and combine the dictionary values - fail_characterized = self.characterize_failures_by_options(self.failing_tests) - fail_simple_diffs = self.check_simple_text_diffs() + fail_characterized = self.characterize_results_by_options(self.failing_tests, 'fail') + fail_simple_diffs = self.check_simple_text_diffs(self.failing_tests, 'fail') flat_combined_dict = self.flatten_and_combine(fail_characterized, fail_simple_diffs) self.save_characterized_file(flat_combined_dict, "fail") @@ -551,45 +582,26 @@ def flatten_and_combine(self, input_dict, input_simple): flat_combined_dict = self.combine_same_sets_of_labels(flat_items) return dict(sort_dict_by_count(flat_combined_dict)) - def characterize_failures_by_options(self, failing_tests): + def characterize_results_by_options(self, test_list, category): # User self.failing_tests, looking at options results = defaultdict(lambda : defaultdict(list)) results['locale'] = {} # Dictionary of labels for each locale - for test in failing_tests: + for test in test_list: # Get input_data, if available - input_data = test.get('input_data') + input_data = test.get('input_data', None) - try: - label = test['label'] - except: - label = '' - key_list = ['locale', 'locale_label', 'option', 'options', - 'language_label', - # Collation - # 'ignorePunctuation', 'compare_result', - 'compare_type', 'test_description', 'unsupported_options', 'rules', 'test_description', - 'warning' - # Number format - 'input_data', - 'notation', 'compactDisplay', 'style', 'currency', 'unit', 'roundingMode', - # list_fmt - 'type', 'input_list', - # date/time format - 'skeleton'] - - option_keys = ['notation', 'compactDisplay', 'style', 'currency', 'unit', 'roundingMode'] + label = test.get('label', '') + key_list = ['locale', 'locale_label', 'option', 'options'] + # Special case for locales for key in key_list: - try: - locale = input_data.get('locale') - except: - locale = None + locale = input_data.get('locale', None) if locale: if locale not in results['locale']: results['locale'][locale] = set() results['locale'][locale].add(label) - options = input_data.get('options') + options = input_data.get('options', None) if options: # Get each combo of key/value for k, value in options.items(): @@ -599,43 +611,41 @@ def characterize_failures_by_options(self, failing_tests): results[k][value] = set() results[k][value].add(label) - # Try fields in lang_names - for key in ['language_label', 'locale_label']: - try: - if input_data.get(key): - value = input_data[key] - if key not in results: - results[key] = {} - if value in results[key]: - results[key][value] = set() - results[key][value].add(label) - except: - continue - - # Try fields in likely_subtags - for key in ['option', 'locale']: - try: - if input_data.get(key): - value = input_data[key] - if key not in results: - results[key] = {} - if value not in results[key]: - results[key][value] = set() - results[key][value].add(label) - except: - continue - - for key in ['language_label', 'ignorePunctuation', 'compare_result', 'compare_type', 'test_description']: - try: - if test.get(key): # For collation results - value = test[key] - if key not in results: - results[key] = {} - if value not in results[key]: - results[key][value] = set() - results[key][value] = set(label) - except: - continue + key_list = ['locale', 'locale_label', 'option', 'options', + 'language_label', + # Collation + # 'ignorePunctuation', 'compare_result', + 'compare_type', 'test_description', 'unsupported_options', 'rules', 'test_description', + 'warning' + # Number format + 'input_data', + 'notation', 'compactDisplay', 'style', 'currency', 'unit', 'roundingMode', + # list_fmt + 'type', 'input_list', + # date/time format + 'skeleton', + 'language_label', 'locale_label', # in lang_names + 'option', 'locale', # in likely_subtags + 'language_label', 'ignorePunctuation', 'compare_result', 'compare_type', 'test_description' + ] + for key in key_list: + if test.get(key, None): # For collation results + value = test[key] + if key not in results: + results[key] = {} + if value not in results[key]: + results[key][value] = set() + results[key][value].add(label) + + ki_key_list = ['known_issue', 'known_issue_id'] + for key in ki_key_list: + if test.get(key, None): # For collation results + value = test[key] + if key not in results: + results[key] = {} + if value not in results[key]: + results[key][value] = set() + results[key][value].add(label) # Look at the input_data part of the test result # TODO: Check the error_detail and error parts, too. @@ -663,11 +673,11 @@ def characterize_failures_by_options(self, failing_tests): # Special case for input_data / options. special_key = 'options' - if input_data and input_data.get(special_key): + if input_data and input_data.get(special_key, None): options = input_data[special_key] self.add_to_results_by_key(label, results, options, test, options.keys()) - error_detail = test.get('error_detail') + error_detail = test.get('error_detail', None) if error_detail: error_keys = error_detail.keys() # ['options'] self.add_to_results_by_key(label, results, error_detail, test, error_keys) @@ -676,52 +686,42 @@ def characterize_failures_by_options(self, failing_tests): # TODO: Add replacing (...) with "-" for numbers # TODO: Find the largest intersections of these sets and sort by size - pass - return results # TODO: Use the following function to update lists. def add_to_results_by_key(self, label, results, input_data, test, key_list): if input_data: for key in key_list: - try: - if input_data.get(key): # For collation results - value = input_data.get(key) - if key == 'input_list': - if 'input_size' not in results: - results['input_size'] = {} - else: - results['input_size'].add(len(value)) - if key == 'rules': - value = 'RULE' # A special case to avoid over-characterization - if key not in results: - results[key] = {} - if value in results[key]: - results[key][value].add(label) + if input_data.get(key, None): # For collation results + value = input_data.get(key, None) + if key == 'input_list': + if 'input_size' not in results: + results['input_size'] = {} else: + results['input_size'].add(len(value)) + if key == 'rules': + value = 'RULE' # A special case to avoid over-characterization + if key not in results: + results[key] = {} + try: + if not results[key].get(value, None): results[key][value] = set() - results[key][value].add(label) - except: - continue - - def check_simple_text_diffs(self): + results[key][value].add(label) + except TypeError as err: + # value may not be hashable. This should be skipped + pass + def check_simple_text_diffs(self, test_list, category): results = defaultdict(list) - results['insert'] = set() - results['delete'] = set() - results['insert_digit'] = set() - results['insert_space'] = set() - results['delete_digit'] = set() - results['delete_space'] = set() - results['replace_digit'] = set() - results['replace_dff'] = set() - results['whitespace_diff'] = set() - results['replace'] = set() - results['parens'] = set() # Substitions of brackets for parens, etc. + all_checks = ['insert', 'delete', 'insert_digit', 'insert_space', 'delete_digit', + 'delete_space', 'replace_digit', 'replace_dff', 'whitespace_diff', + 'replace', 'parens'] + for check in all_checks: + results[check] = set() - for fail in self.failing_tests: + for fail in test_list: label = fail['label'] - actual = fail.get('result') - expected = fail.get('expected') + actual = fail.get('result', None) + expected = fail.get('expected', None) if (actual is None) or (expected is None): continue # Special case for differing by a single character. @@ -954,16 +954,16 @@ def fill_templates(self, result_data, result_cases, result_table_template, summa # For filling in templates for cases of passing, failing, errors, unsupported, known_issue result_class = result_data.name section_name = '%s_section' % result_class - summary_name = '%s_summary % result_class' + summary_name = '%s_summary' % result_class # TODO: Call this for each cases instead of duplicated lines if result_cases: case_lines = [] - test_table_name = 'test_%s_' % result_class + test_table_name = 'test_%s' % result_class options_name = '%s_options' % result_class options_string = '%s options' % result_class - # Create a table of all test errors. + # Create a table of all test results in this category. for unsupported in result_cases: line = result_data.result_table_template.safe_substitute(unsupported) case_lines.append(line) @@ -981,7 +981,7 @@ def fill_templates(self, result_data, result_cases, result_table_template, summa # ??? TODO: examine if "error" is correct below for key, labels in case_summary.items(): count = len(labels) - sub = {'error': key, 'count': count} + sub = {'known_issue': key, 'count': count} summary_lines.append( result_data.summary_template.safe_substitute(sub) ) @@ -1103,6 +1103,7 @@ def summarize_reports(self): 'pass_count': int(test_json['passCount']), 'error_count': int(test_json['test_error_count']), 'unsupported_count': len(test_json['unsupported']), + 'known_issue_count': int(test_json['known_issue_count']), 'missing_verify_count': len(test_json['missing_verify_data']), 'json_file_name': filename, 'html_file_name': relative_html_path, # Relative to the report base @@ -1110,13 +1111,6 @@ def summarize_reports(self): 'icu_version': icu_version, 'platform_version': '%s %s' % (platform['platform'], platform['platformVersion']) } - - # Handle this sepparately for now as we add known issue support - try: - test_results['known_issue_count'] = len(test_json['known_issue']) - except BaseException as err: - test_results['known_issue_count'] = 0 - except BaseException as err: logging.error('SUMMARIZE REPORTS for file %s. Error: %s' % (filename, err)) diff --git a/verifier/verifier.py b/verifier/verifier.py index 7df28707..da6e890b 100644 --- a/verifier/verifier.py +++ b/verifier/verifier.py @@ -47,6 +47,8 @@ def __init__(self): self.report = None self.reports = [] + self.run_in_parallel = True + # Set of [result filepath, verify filepath, report path] self.result_timestamp = None @@ -222,15 +224,20 @@ def setup_verify_plans(self): # Verify plans in parallel def parallel_verify_data_results(self): - num_processors = mp.cpu_count() - verify_plans = self.verify_plans - logging.info('JSON validation: %s processors for %s plans', - num_processors, len(verify_plans)) - - processor_pool = mp.Pool(num_processors) - with processor_pool as p: - result = p.map(self.verify_one_plan, verify_plans) - return result + if not self.options.run_serial: + num_processors = mp.cpu_count() + verify_plans = self.verify_plans + logging.info('JSON validation: %s processors for %s plans', + num_processors, len(verify_plans)) + + processor_pool = mp.Pool(num_processors) + with processor_pool as p: + result = p.map(self.verify_one_plan, verify_plans) + return result + else: + logging.info('Running serially!') + for vplan in self.verify_plans: + self.verify_one_plan(vplan) # For one VerifyPlan, get data and run verification def verify_one_plan(self, vplan): @@ -249,16 +256,19 @@ def verify_one_plan(self, vplan): vplan.setup_report_data() result = {'compare_success': True} + + # Do more analysis on the failures and compute known issues + vplan.report.summarize_failures() + + vplan.report.add_known_issues() # Save the results + if not vplan.report.save_report(): logging.error('!!! Could not save report for (%s, %s)', vplan.test_type, vplan.exec) else: vplan.report.create_html_report() - # Do more analysis on the failures - vplan.report.summarize_failures() - logging.debug('\nTEST RESULTS in %s for %s. %d tests found', vplan.exec, vplan.test_type, len(vplan.test_results))
' + output +'' + output +'