@@ -46,6 +46,9 @@ public class TestUnicodeInvariants {
4646 private static int showRangeLimit = 20 ;
4747 static boolean doHtml = true ;
4848 public static final String DEFAULT_FILE = "UnicodeInvariantTest.txt" ;
49+ public static final HTMLTabber htmlTabber = new Tabber .HTMLTabber ();
50+ public static final boolean EMIT_GITHUB_ERRORS =
51+ System .getProperty ("EMIT_GITHUB_ERRORS" ) != null ;
4952
5053 private static final int
5154 // HELP1 = 0,
@@ -171,8 +174,6 @@ public static int testInvariants(String inputFile, boolean doRange) throws IOExc
171174 out3 .write ('\uFEFF' ); // BOM
172175 }
173176 try (final BufferedReader in = getInputReader (inputFile )) {
174- final HTMLTabber tabber = new Tabber .HTMLTabber ();
175-
176177 errorLister =
177178 new BagFormatter ()
178179 .setMergeRanges (doRange )
@@ -183,7 +184,7 @@ public static int testInvariants(String inputFile, boolean doRange) throws IOExc
183184 .setFixName (toHTML );
184185 errorLister .setShowTotal (false );
185186 if (doHtml ) {
186- errorLister .setTabber (tabber );
187+ errorLister .setTabber (htmlTabber );
187188 }
188189
189190 showLister =
@@ -198,7 +199,7 @@ public static int testInvariants(String inputFile, boolean doRange) throws IOExc
198199 showLister .setValueSource (LATEST_PROPS .getProperty ("script" ));
199200 }
200201 if (doHtml ) {
201- showLister .setTabber (tabber );
202+ showLister .setTabber (htmlTabber );
202203 }
203204
204205 // symbolTable = new ChainedSymbolTable();
@@ -207,7 +208,7 @@ public static int testInvariants(String inputFile, boolean doRange) throws IOExc
207208 // ToolUnicodePropertySource.make(UCD.lastVersion).getSymbolTable("\u00D7"),
208209 //
209210 // ToolUnicodePropertySource.make(Default.ucdVersion()).getSymbolTable("")});
210- while ( true ) {
211+ for ( int lineNumber = 1 ; ; ++ lineNumber ) {
211212 String line = in .readLine ();
212213 if (line == null ) {
213214 break ;
@@ -230,24 +231,24 @@ public static int testInvariants(String inputFile, boolean doRange) throws IOExc
230231 } else if (line .startsWith ("Let" )) {
231232 letLine (pp , line );
232233 } else if (line .startsWith ("In" )) {
233- inLine (pp , line );
234+ inLine (pp , line , lineNumber );
234235 } else if (line .startsWith ("ShowScript" )) {
235236 showScript = true ;
236237 } else if (line .startsWith ("HideScript" )) {
237238 showScript = false ;
238239 } else if (line .startsWith ("Map" )) {
239- testMapLine (line , pp );
240+ testMapLine (line , pp , lineNumber );
240241 } else if (line .startsWith ("ShowMap" )) {
241242 showMapLine (line , pp );
242243 } else if (line .startsWith ("Show" )) {
243244 showLine (line , pp );
244245 } else if (line .startsWith ("EquivalencesOf" )) {
245- equivalencesLine (line , pp );
246+ equivalencesLine (line , pp , lineNumber );
246247 } else {
247- testLine (line , pp );
248+ testLine (line , pp , lineNumber );
248249 }
249250 } catch (final Exception e ) {
250- parseErrorCount = parseError (parseErrorCount , line , e );
251+ parseErrorCount = parseError (parseErrorCount , line , e , lineNumber );
251252 continue ;
252253 }
253254 }
@@ -276,7 +277,8 @@ static class PropertyComparison {
276277 UnicodeProperty property2 ;
277278 }
278279
279- private static void equivalencesLine (String line , ParsePosition pp ) throws ParseException {
280+ private static void equivalencesLine (String line , ParsePosition pp , int lineNumber )
281+ throws ParseException {
280282 pp .setIndex ("EquivalencesOf" .length ());
281283 final UnicodeSet domain = new UnicodeSet (line , pp , symbolTable );
282284 final var leftProperty = CompoundProperty .of (LATEST_PROPS , line , pp );
@@ -435,16 +437,26 @@ private static void equivalencesLine(String line, ParsePosition pp) throws Parse
435437 ++testFailureCount ;
436438 printErrorLine ("Test Failure" , Side .START , testFailureCount );
437439 }
440+ final List <String > errorMessageLines = new ArrayList <>();
438441 if (counterexamples .isEmpty ()) {
439- println ("There are no counterexamples to " + relationOperator + "." );
442+ errorMessageLines . add ("There are no counterexamples to " + relationOperator + "." );
440443 } else {
441444 if (leftShouldImplyRight ) {
442- println ("The implication ⇒ is " + leftImpliesRightCounterexamples .isEmpty () + "." );
445+ errorMessageLines .add (
446+ "The implication ⇒ is " + leftImpliesRightCounterexamples .isEmpty () + "." );
443447 }
444448 if (rightShouldImplyLeft ) {
445- println ("The implication ⇐ is " + rightImpliesLeftCounterexamples .isEmpty () + "." );
449+ errorMessageLines .add (
450+ "The implication ⇐ is " + rightImpliesLeftCounterexamples .isEmpty () + "." );
446451 }
447452 }
453+ for (var errorLine : errorMessageLines ) {
454+ println (errorLine );
455+ }
456+ errorMessageLines .addAll (counterexamples );
457+ if (failure ) {
458+ reportTestFailure (lineNumber , String .join ("\n " , errorMessageLines ).replace ('\t' , ' ' ));
459+ }
448460 out .println (failure ? "<table class='f'>" : "<table>" );
449461 for (String counterexample : counterexamples ) {
450462 out .println ("<tr><td>" );
@@ -457,7 +469,8 @@ private static void equivalencesLine(String line, ParsePosition pp) throws Parse
457469 }
458470 }
459471
460- private static void inLine (ParsePosition pp , String line ) throws ParseException {
472+ private static void inLine (ParsePosition pp , String line , int lineNumber )
473+ throws ParseException {
461474 pp .setIndex (2 );
462475 final PropertyComparison propertyComparison = getPropertyComparison (pp , line );
463476 final UnicodeMap <String > failures = new UnicodeMap <>();
@@ -476,13 +489,23 @@ private static void inLine(ParsePosition pp, String line) throws ParseException
476489 if (failureCount != 0 ) {
477490 testFailureCount ++;
478491 printErrorLine ("Test Failure" , Side .START , testFailureCount );
479- println (
480- "## Got unexpected "
492+ String errorMessage =
493+ "Got unexpected "
481494 + (propertyComparison .shouldBeEqual ? "differences" : "equalities" )
482495 + ": "
483- + failureCount );
496+ + failureCount ;
497+ println ("## " + errorMessage );
498+
484499 final UnicodeLabel failureProp = new UnicodeProperty .UnicodeMapProperty ().set (failures );
485500 errorLister .setValueSource (failureProp );
501+
502+ var monoTable = new StringWriter ();
503+ errorLister .setTabber (new Tabber .MonoTabber ());
504+ errorLister .setLineSeparator ("\n " );
505+ errorLister .showSetNames (new PrintWriter (monoTable ), failureSet );
506+ errorLister .setTabber (htmlTabber );
507+ reportTestFailure (lineNumber , errorMessage + "\n " + monoTable .toString ());
508+
486509 if (doHtml ) {
487510 out .println ("<table class='f'>" );
488511 }
@@ -710,7 +733,8 @@ private static void showMapLine(String line, ParsePosition pp) {
710733 showLister .setMergeRanges (doRange );
711734 }
712735
713- private static void testLine (String line , ParsePosition pp ) throws ParseException {
736+ private static void testLine (String line , ParsePosition pp , int lineNumber )
737+ throws ParseException {
714738 if (line .startsWith ("Test" )) {
715739 line = line .substring (4 ).trim ();
716740 }
@@ -776,21 +800,24 @@ private static void testLine(String line, ParsePosition pp) throws ParseExceptio
776800 "In" ,
777801 rightSide ,
778802 "But Not In" ,
779- leftSide );
803+ leftSide ,
804+ lineNumber );
780805 checkExpected (
781806 rightAndLeft ,
782807 new UnicodeSet (rightSet ).retainAll (leftSet ),
783808 "In" ,
784809 rightSide ,
785810 "And In" ,
786- leftSide );
811+ leftSide ,
812+ lineNumber );
787813 checkExpected (
788814 left_right ,
789815 new UnicodeSet (leftSet ).removeAll (rightSet ),
790816 "In" ,
791817 leftSide ,
792818 "But Not In" ,
793- rightSide );
819+ rightSide ,
820+ lineNumber );
794821 }
795822
796823 public static void checkRelation (ParsePosition pp , char relation ) throws ParseException {
@@ -810,7 +837,8 @@ private static void checkExpected(
810837 String rightStatus ,
811838 String rightSide ,
812839 String leftStatus ,
813- String leftSide ) {
840+ String leftSide ,
841+ int lineNumber ) {
814842 switch (expected ) {
815843 case empty :
816844 if (segment .size () == 0 ) {
@@ -829,9 +857,22 @@ private static void checkExpected(
829857 }
830858 testFailureCount ++;
831859 printErrorLine ("Test Failure" , Side .START , testFailureCount );
832- println ("## Expected " + expected + ", got: " + segment .size () + "\t " + segment .toString ());
833- println ("## " + rightStatus + "\t " + rightSide );
834- println ("## " + leftStatus + "\t " + leftSide );
860+ final var errorMessageLines =
861+ new String [] {
862+ "Expected " + expected + ", got: " + segment .size () + "\t " + segment .toString (),
863+ rightStatus + "\t " + rightSide ,
864+ leftStatus + "\t " + leftSide
865+ };
866+ var monoTable = new StringWriter ();
867+ for (String line : errorMessageLines ) {
868+ println ("## " + line );
869+ }
870+ errorLister .setTabber (new Tabber .MonoTabber ());
871+ errorLister .setLineSeparator ("\n " );
872+ errorLister .showSetNames (new PrintWriter (monoTable ), segment );
873+ reportTestFailure (
874+ lineNumber , String .join ("\n " , errorMessageLines ) + "\n " + monoTable .toString ());
875+ errorLister .setTabber (htmlTabber );
835876 if (doHtml ) {
836877 out .println ("<table class='e'>" );
837878 }
@@ -853,7 +894,8 @@ private static void checkExpected(
853894 getProperties (Settings .lastVersion ),
854895 IndexUnicodeProperties .make (Settings .lastVersion )));
855896
856- private static void testMapLine (String line , ParsePosition pp ) throws ParseException {
897+ private static void testMapLine (String line , ParsePosition pp , int lineNumber )
898+ throws ParseException {
857899 char relation = 0 ;
858900 String rightSide = null ;
859901 String leftSide = null ;
@@ -915,21 +957,24 @@ private static void testMapLine(String line, ParsePosition pp) throws ParseExcep
915957 "In" ,
916958 rightSide ,
917959 "But Not In" ,
918- leftSide );
960+ leftSide ,
961+ lineNumber );
919962 checkExpected (
920963 rightAndLeft ,
921964 UnicodeMapParser .retainAll (new UnicodeMap <String >().putAll (rightSet ), leftSet ),
922965 "In" ,
923966 rightSide ,
924967 "And In" ,
925- leftSide );
968+ leftSide ,
969+ lineNumber );
926970 checkExpected (
927971 left_right ,
928972 UnicodeMapParser .removeAll (new UnicodeMap <String >().putAll (leftSet ), rightSet ),
929973 "In" ,
930974 leftSide ,
931975 "But Not In" ,
932- rightSide );
976+ rightSide ,
977+ lineNumber );
933978 }
934979
935980 private static void checkExpected (
@@ -938,7 +983,8 @@ private static void checkExpected(
938983 String rightStatus ,
939984 String rightSide ,
940985 String leftStatus ,
941- String leftSide ) {
986+ String leftSide ,
987+ int lineNumber ) {
942988 switch (expected ) {
943989 case empty :
944990 if (segment .size () == 0 ) {
@@ -1015,7 +1061,7 @@ private static void showSet(ParsePosition pp, final String value) {
10151061 println ();
10161062 }
10171063
1018- private static int parseError (int parseErrorCount , String line , Exception e ) {
1064+ private static int parseError (int parseErrorCount , String line , Exception e , int lineNumber ) {
10191065 parseErrorCount ++;
10201066 if (e instanceof ParseException ) {
10211067 final int index = ((ParseException ) e ).getErrorOffset ();
@@ -1029,7 +1075,9 @@ private static int parseError(int parseErrorCount, String line, Exception e) {
10291075 if (message != null ) {
10301076 println ("##" + message );
10311077 }
1078+ reportParseError (lineNumber , message );
10321079 e .printStackTrace (out );
1080+
10331081 out .println ("</pre>" );
10341082 printErrorLine ("Parse Error" , Side .END , parseErrorCount );
10351083 println ();
@@ -1122,6 +1170,28 @@ private static void println() {
11221170 println ("" );
11231171 }
11241172
1173+ private static void reportParseError (int lineNumber , String message ) {
1174+ reportError (lineNumber , "Parse error" , message );
1175+ }
1176+
1177+ private static void reportTestFailure (int lineNumber , String message ) {
1178+ reportError (lineNumber , "Invariant test failure" , message );
1179+ }
1180+
1181+ private static void reportError (int lineNumber , String title , String message ) {
1182+ if (EMIT_GITHUB_ERRORS ) {
1183+ System .err .println (
1184+ "::error file=unicodetools/src/main/resources/org/unicode/text/UCD/"
1185+ + DEFAULT_FILE
1186+ + ",line="
1187+ + lineNumber
1188+ + ",title="
1189+ + title
1190+ + "::"
1191+ + message .replace ("%" , "%25" ).replace ("\n " , "%0A" ));
1192+ }
1193+ }
1194+
11251195 /** Should add to UnicodeSet */
11261196 public static String scan (UnicodeSet unicodeSet , String line , ParsePosition pp , boolean in ) {
11271197 final int start = pp .getIndex ();
0 commit comments