1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 1999-2004 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *     http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.regexp.internal;
  22 
  23 import java.io.BufferedReader;
  24 import java.io.FileReader;
  25 import java.io.InputStreamReader;
  26 import java.io.PrintWriter;
  27 import java.io.File;
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.ObjectOutputStream;
  30 import java.io.ByteArrayInputStream;
  31 import java.io.ObjectInputStream;
  32 import java.io.StringBufferInputStream;
  33 import java.io.StringReader;
  34 import java.io.IOException;
  35 
  36 /**
  37  * Data driven (and optionally interactive) testing harness to exercise regular
  38  * expression compiler and matching engine.
  39  *
  40  * @author <a href="mailto:jonl@muppetlabs.com">Jonathan Locke</a>
  41  * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
  42  * @author <a href="mailto:gholam@xtra.co.nz">Michael McCallum</a>
  43  */
  44 public class RETest
  45 {
  46     // True if we want to see output from success cases
  47     static final boolean showSuccesses = false;
  48 
  49     // A new line character.
  50     static final String NEW_LINE = System.getProperty( "line.separator" );
  51 
  52     // Construct a debug compiler
  53     REDebugCompiler compiler = new REDebugCompiler();
  54 
  55     /**
  56      * Main program entrypoint.  If an argument is given, it will be compiled
  57      * and interactive matching will ensue.  If no argument is given, the
  58      * file RETest.txt will be used as automated testing input.
  59      * @param args Command line arguments (optional regular expression)
  60      */
  61     public static void main(String[] args)
  62     {
  63         try
  64         {
  65             if (!test( args )) {
  66                 System.exit(1);
  67             }
  68         }
  69         catch (Exception e)
  70         {
  71             e.printStackTrace();
  72             System.exit(1);
  73         }
  74     }
  75 
  76     /**
  77      * Testing entrypoint.
  78      * @param args Command line arguments
  79      * @exception Exception thrown in case of error
  80      */
  81     public static boolean test( String[] args ) throws Exception
  82     {
  83         RETest test = new RETest();
  84         // Run interactive tests against a single regexp
  85         if (args.length == 2)
  86         {
  87             test.runInteractiveTests(args[1]);
  88         }
  89         else if (args.length == 1)
  90         {
  91             // Run automated tests
  92             test.runAutomatedTests(args[0]);
  93         }
  94         else
  95         {
  96             System.out.println( "Usage: RETest ([-i] [regex]) ([/path/to/testfile.txt])" );
  97             System.out.println( "By Default will run automated tests from file 'docs/RETest.txt' ..." );
  98             System.out.println();
  99             test.runAutomatedTests("docs/RETest.txt");
 100         }
 101         return test.failures == 0;
 102     }
 103 
 104     /**
 105      * Constructor
 106      */
 107     public RETest()
 108     {
 109     }
 110 
 111     /**
 112      * Compile and test matching against a single expression
 113      * @param expr Expression to compile and test
 114      */
 115     void runInteractiveTests(String expr)
 116     {
 117         RE r = new RE();
 118         try
 119         {
 120             // Compile expression
 121             r.setProgram(compiler.compile(expr));
 122 
 123             // Show expression
 124             say("" + NEW_LINE + "" + expr + "" + NEW_LINE + "");
 125 
 126             // Show program for compiled expression
 127             PrintWriter writer = new PrintWriter( System.out );
 128             compiler.dumpProgram( writer );
 129             writer.flush();
 130 
 131             boolean running = true;
 132             // Test matching against compiled expression
 133             while ( running )
 134             {
 135                 // Read from keyboard
 136                 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 137                 System.out.print("> ");
 138                 System.out.flush();
 139                 String match = br.readLine();
 140 
 141                 if ( match != null )
 142                 {
 143                     // Try a match against the keyboard input
 144                     if (r.match(match))
 145                     {
 146                         say("Match successful.");
 147                     }
 148                     else
 149                     {
 150                         say("Match failed.");
 151                     }
 152 
 153                     // Show subparen registers
 154                     showParens(r);
 155                 }
 156                 else
 157                 {
 158                     running = false;
 159                     System.out.println();
 160                 }
 161             }
 162         }
 163         catch (Exception e)
 164         {
 165             say("Error: " + e.toString());
 166             e.printStackTrace();
 167         }
 168     }
 169 
 170     /**
 171      * Exit with a fatal error.
 172      * @param s Last famous words before exiting
 173      */
 174     void die(String s)
 175     {
 176         say("FATAL ERROR: " + s);
 177         System.exit(-1);
 178     }
 179 
 180     /**
 181      * Fail with an error. Will print a big failure message to System.out.
 182      *
 183      * @param log Output before failure
 184      * @param s Failure description
 185      */
 186     void fail(StringBuffer log, String s)
 187     {
 188         System.out.print(log.toString());
 189         fail(s);
 190     }
 191 
 192     /**
 193      * Fail with an error. Will print a big failure message to System.out.
 194      *
 195      * @param s Failure description
 196      */
 197     void fail(String s)
 198     {
 199         failures++;
 200         say("" + NEW_LINE + "");
 201         say("*******************************************************");
 202         say("*********************  FAILURE!  **********************");
 203         say("*******************************************************");
 204         say("" + NEW_LINE + "");
 205         say(s);
 206         say("");
 207         // make sure the writer gets flushed.
 208         if (compiler != null) {
 209             PrintWriter writer = new PrintWriter( System.out );
 210             compiler.dumpProgram( writer );
 211             writer.flush();
 212             say("" + NEW_LINE + "");
 213         }
 214     }
 215 
 216     /**
 217      * Say something to standard out
 218      * @param s What to say
 219      */
 220     void say(String s)
 221     {
 222         System.out.println(s);
 223     }
 224 
 225     /**
 226      * Dump parenthesized subexpressions found by a regular expression matcher object
 227      * @param r Matcher object with results to show
 228      */
 229     void showParens(RE r)
 230     {
 231         // Loop through each paren
 232         for (int i = 0; i < r.getParenCount(); i++)
 233         {
 234             // Show paren register
 235             say("$" + i + " = " + r.getParen(i));
 236         }
 237     }
 238 
 239     /*
 240      * number in automated test
 241      */
 242     int testCount = 0;
 243 
 244     /*
 245      * Count of failures in automated test
 246      */
 247     int failures = 0;
 248 
 249     /**
 250      * Run automated tests in RETest.txt file (from Perl 4.0 test battery)
 251      * @exception Exception thrown in case of error
 252      */
 253     void runAutomatedTests(String testDocument) throws Exception
 254     {
 255         long ms = System.currentTimeMillis();
 256 
 257         // Some unit tests
 258         testPrecompiledRE();
 259         testSplitAndGrep();
 260         testSubst();
 261         testOther();
 262 
 263         // Test from script file
 264         File testInput = new File(testDocument);
 265         if (! testInput.exists()) {
 266             throw new Exception ("Could not find: " + testDocument);
 267         }
 268 
 269         BufferedReader br = new BufferedReader(new FileReader(testInput));
 270         try
 271         {
 272             // While input is available, parse lines
 273             while (br.ready())
 274             {
 275                 RETestCase testcase = getNextTestCase(br);
 276                 if (testcase != null) {
 277                     testcase.runTest();
 278                 }
 279             }
 280         }
 281         finally
 282         {
 283             br.close();
 284         }
 285 
 286         // Show match time
 287         say(NEW_LINE + NEW_LINE + "Match time = " + (System.currentTimeMillis() - ms) + " ms.");
 288 
 289         // Print final results
 290         if (failures > 0) {
 291             say("*************** THERE ARE FAILURES! *******************");
 292         }
 293         say("Tests complete.  " + testCount + " tests, " + failures + " failure(s).");
 294     }
 295 
 296     /**
 297      * Run automated unit test
 298      * @exception Exception thrown in case of error
 299      */
 300     void testOther() throws Exception
 301     {
 302         // Serialization test 1: Compile regexp and serialize/deserialize it
 303         RE r = new RE("(a*)b");
 304         say("Serialized/deserialized (a*)b");
 305         ByteArrayOutputStream out = new ByteArrayOutputStream(128);
 306         new ObjectOutputStream(out).writeObject(r);
 307         ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 308         r = (RE)new ObjectInputStream(in).readObject();
 309         if (!r.match("aaab"))
 310         {
 311             fail("Did not match 'aaab' with deserialized RE.");
 312         } else {
 313             say("aaaab = true");
 314             showParens(r);
 315         }
 316 
 317         // Serialization test 2: serialize/deserialize used regexp
 318         out.reset();
 319         say("Deserialized (a*)b");
 320         new ObjectOutputStream(out).writeObject(r);
 321         in = new ByteArrayInputStream(out.toByteArray());
 322         r = (RE)new ObjectInputStream(in).readObject();
 323         if (r.getParenCount() != 0)
 324         {
 325             fail("Has parens after deserialization.");
 326         }
 327         if (!r.match("aaab"))
 328         {
 329             fail("Did not match 'aaab' with deserialized RE.");
 330         } else {
 331             say("aaaab = true");
 332             showParens(r);
 333         }
 334 
 335         // Test MATCH_CASEINDEPENDENT
 336         r = new RE("abc(\\w*)");
 337         say("MATCH_CASEINDEPENDENT abc(\\w*)");
 338         r.setMatchFlags(RE.MATCH_CASEINDEPENDENT);
 339         say("abc(d*)");
 340         if (!r.match("abcddd"))
 341         {
 342             fail("Did not match 'abcddd'.");
 343         } else {
 344             say("abcddd = true");
 345             showParens(r);
 346         }
 347 
 348         if (!r.match("aBcDDdd"))
 349         {
 350             fail("Did not match 'aBcDDdd'.");
 351         } else {
 352             say("aBcDDdd = true");
 353             showParens(r);
 354         }
 355 
 356         if (!r.match("ABCDDDDD"))
 357         {
 358             fail("Did not match 'ABCDDDDD'.");
 359         } else {
 360             say("ABCDDDDD = true");
 361             showParens(r);
 362         }
 363 
 364         r = new RE("(A*)b\\1");
 365         r.setMatchFlags(RE.MATCH_CASEINDEPENDENT);
 366         if (!r.match("AaAaaaBAAAAAA"))
 367         {
 368             fail("Did not match 'AaAaaaBAAAAAA'.");
 369         } else {
 370             say("AaAaaaBAAAAAA = true");
 371             showParens(r);
 372         }
 373 
 374         r = new RE("[A-Z]*");
 375         r.setMatchFlags(RE.MATCH_CASEINDEPENDENT);
 376         if (!r.match("CaBgDe12"))
 377         {
 378             fail("Did not match 'CaBgDe12'.");
 379         } else {
 380             say("CaBgDe12 = true");
 381             showParens(r);
 382         }
 383 
 384         // Test MATCH_MULTILINE. Test for eol/bol symbols.
 385         r = new RE("^abc$", RE.MATCH_MULTILINE);
 386         if (!r.match("\nabc")) {
 387             fail("\"\\nabc\" doesn't match \"^abc$\"");
 388         }
 389         if (!r.match("\rabc")) {
 390             fail("\"\\rabc\" doesn't match \"^abc$\"");
 391         }
 392         if (!r.match("\r\nabc")) {
 393             fail("\"\\r\\nabc\" doesn't match \"^abc$\"");
 394         }
 395         if (!r.match("\u0085abc")) {
 396             fail("\"\\u0085abc\" doesn't match \"^abc$\"");
 397         }
 398         if (!r.match("\u2028abc")) {
 399             fail("\"\\u2028abc\" doesn't match \"^abc$\"");
 400         }
 401         if (!r.match("\u2029abc")) {
 402             fail("\"\\u2029abc\" doesn't match \"^abc$\"");
 403         }
 404 
 405         // Test MATCH_MULTILINE. Test that '.' does not matches new line.
 406         r = new RE("^a.*b$", RE.MATCH_MULTILINE);
 407         if (r.match("a\nb")) {
 408             fail("\"a\\nb\" matches \"^a.*b$\"");
 409         }
 410         if (r.match("a\rb")) {
 411             fail("\"a\\rb\" matches \"^a.*b$\"");
 412         }
 413         if (r.match("a\r\nb")) {
 414             fail("\"a\\r\\nb\" matches \"^a.*b$\"");
 415         }
 416         if (r.match("a\u0085b")) {
 417             fail("\"a\\u0085b\" matches \"^a.*b$\"");
 418         }
 419         if (r.match("a\u2028b")) {
 420             fail("\"a\\u2028b\" matches \"^a.*b$\"");
 421         }
 422         if (r.match("a\u2029b")) {
 423             fail("\"a\\u2029b\" matches \"^a.*b$\"");
 424         }
 425     }
 426 
 427     private void testPrecompiledRE()
 428     {
 429         // Pre-compiled regular expression "a*b"
 430         char[] re1Instructions =
 431         {
 432             0x007c, 0x0000, 0x001a, 0x007c, 0x0000, 0x000d, 0x0041,
 433             0x0001, 0x0004, 0x0061, 0x007c, 0x0000, 0x0003, 0x0047,
 434             0x0000, 0xfff6, 0x007c, 0x0000, 0x0003, 0x004e, 0x0000,
 435             0x0003, 0x0041, 0x0001, 0x0004, 0x0062, 0x0045, 0x0000,
 436             0x0000,
 437         };
 438 
 439         REProgram re1 = new REProgram(re1Instructions);
 440 
 441         // Simple test of pre-compiled regular expressions
 442         RE r = new RE(re1);
 443         say("a*b");
 444         boolean result = r.match("aaab");
 445         say("aaab = " + result);
 446         showParens(r);
 447         if (!result) {
 448             fail("\"aaab\" doesn't match to precompiled \"a*b\"");
 449         }
 450 
 451         result = r.match("b");
 452         say("b = " + result);
 453         showParens(r);
 454         if (!result) {
 455             fail("\"b\" doesn't match to precompiled \"a*b\"");
 456         }
 457 
 458         result = r.match("c");
 459         say("c = " + result);
 460         showParens(r);
 461         if (result) {
 462             fail("\"c\" matches to precompiled \"a*b\"");
 463         }
 464 
 465         result = r.match("ccccaaaaab");
 466         say("ccccaaaaab = " + result);
 467         showParens(r);
 468         if (!result) {
 469             fail("\"ccccaaaaab\" doesn't match to precompiled \"a*b\"");
 470         }
 471     }
 472 
 473     private void testSplitAndGrep()
 474     {
 475         String[] expected = {"xxxx", "xxxx", "yyyy", "zzz"};
 476         RE r = new RE("a*b");
 477         String[] s = r.split("xxxxaabxxxxbyyyyaaabzzz");
 478         for (int i = 0; i < expected.length && i < s.length; i++) {
 479             assertEquals("Wrong splitted part", expected[i], s[i]);
 480         }
 481         assertEquals("Wrong number of splitted parts", expected.length,
 482                      s.length);
 483 
 484         r = new RE("x+");
 485         expected = new String[] {"xxxx", "xxxx"};
 486         s = r.grep(s);
 487         for (int i = 0; i < s.length; i++)
 488         {
 489             say("s[" + i + "] = " + s[i]);
 490             assertEquals("Grep fails", expected[i], s[i]);
 491         }
 492         assertEquals("Wrong number of string found by grep", expected.length,
 493                      s.length);
 494     }
 495 
 496     private void testSubst()
 497     {
 498         RE r = new RE("a*b");
 499         String expected = "-foo-garply-wacky-";
 500         String actual = r.subst("aaaabfooaaabgarplyaaabwackyb", "-");
 501         assertEquals("Wrong result of substitution in \"a*b\"", expected, actual);
 502 
 503         // Test subst() with backreferences
 504         r = new RE("http://[\\.\\w\\-\\?/~_@&=%]+");
 505         actual = r.subst("visit us: http://www.apache.org!",
 506                          "1234<a href=\"$0\">$0</a>", RE.REPLACE_BACKREFERENCES);
 507         assertEquals("Wrong subst() result", "visit us: 1234<a href=\"http://www.apache.org\">http://www.apache.org</a>!", actual);
 508 
 509         // Test subst() with backreferences without leading characters
 510         // before first backreference
 511         r = new RE("(.*?)=(.*)");
 512         actual = r.subst("variable=value",
 513                          "$1_test_$212", RE.REPLACE_BACKREFERENCES);
 514         assertEquals("Wrong subst() result", "variable_test_value12", actual);
 515 
 516         // Test subst() with NO backreferences
 517         r = new RE("^a$");
 518         actual = r.subst("a",
 519                          "b", RE.REPLACE_BACKREFERENCES);
 520         assertEquals("Wrong subst() result", "b", actual);
 521 
 522         // Test subst() with NO backreferences
 523         r = new RE("^a$", RE.MATCH_MULTILINE);
 524         actual = r.subst("\r\na\r\n",
 525                          "b", RE.REPLACE_BACKREFERENCES);
 526         assertEquals("Wrong subst() result", "\r\nb\r\n", actual);
 527     }
 528 
 529     public void assertEquals(String message, String expected, String actual)
 530     {
 531         if (expected != null && !expected.equals(actual)
 532             || actual != null && !actual.equals(expected))
 533         {
 534             fail(message + " (expected \"" + expected
 535                  + "\", actual \"" + actual + "\")");
 536         }
 537     }
 538 
 539     public void assertEquals(String message, int expected, int actual)
 540     {
 541         if (expected != actual) {
 542             fail(message + " (expected \"" + expected
 543                  + "\", actual \"" + actual + "\")");
 544         }
 545     }
 546 
 547     /**
 548      * Converts yesno string to boolean.
 549      * @param yesno string representation of expected result
 550      * @return true if yesno is "YES", false if yesno is "NO"
 551      *         stops program otherwise.
 552      */
 553     private boolean getExpectedResult(String yesno)
 554     {
 555         if ("NO".equals(yesno))
 556         {
 557             return false;
 558         }
 559         else if ("YES".equals(yesno))
 560         {
 561             return true;
 562         }
 563         else
 564         {
 565             // Bad test script
 566             die("Test script error!");
 567             return false; //to please javac
 568         }
 569     }
 570 
 571     /**
 572      * Finds next test description in a given script.
 573      * @param br <code>BufferedReader</code> for a script file
 574      * @return strign tag for next test description
 575      * @exception IOException if some io problems occured
 576      */
 577     private String findNextTest(BufferedReader br) throws IOException
 578     {
 579         String number = "";
 580 
 581         while (br.ready())
 582         {
 583             number = br.readLine();
 584             if (number == null)
 585             {
 586                 break;
 587             }
 588             number = number.trim();
 589             if (number.startsWith("#"))
 590             {
 591                 break;
 592             }
 593             if (!number.equals(""))
 594             {
 595                 say("Script error.  Line = " + number);
 596                 System.exit(-1);
 597             }
 598         }
 599         return number;
 600     }
 601 
 602     /**
 603      * Creates testcase for the next test description in the script file.
 604      * @param br <code>BufferedReader</code> for script file.
 605      * @return a new tescase or null.
 606      * @exception IOException if some io problems occured
 607      */
 608     private RETestCase getNextTestCase(BufferedReader br) throws IOException
 609     {
 610         // Find next re test case
 611         final String tag = findNextTest(br);
 612 
 613         // Are we done?
 614         if (!br.ready())
 615         {
 616             return null;
 617         }
 618 
 619         // Get expression
 620         final String expr = br.readLine();
 621 
 622         // Get test information
 623         final String matchAgainst = br.readLine();
 624         final boolean badPattern = "ERR".equals(matchAgainst);
 625         boolean shouldMatch = false;
 626         int expectedParenCount = 0;
 627         String[] expectedParens = null;
 628 
 629         if (!badPattern) {
 630             shouldMatch = getExpectedResult(br.readLine().trim());
 631             if (shouldMatch) {
 632                 expectedParenCount = Integer.parseInt(br.readLine().trim());
 633                 expectedParens = new String[expectedParenCount];
 634                 for (int i = 0; i < expectedParenCount; i++) {
 635                     expectedParens[i] = br.readLine();
 636                 }
 637             }
 638         }
 639 
 640         return new RETestCase(this, tag, expr, matchAgainst, badPattern,
 641                               shouldMatch, expectedParens);
 642     }
 643 }
 644 
 645 final class RETestCase
 646 {
 647     final private StringBuffer log = new StringBuffer();
 648     final private int number;
 649     final private String tag; // number from script file
 650     final private String pattern;
 651     final private String toMatch;
 652     final private boolean badPattern;
 653     final private boolean shouldMatch;
 654     final private String[] parens;
 655     final private RETest test;
 656     private RE regexp;
 657 
 658     public RETestCase(RETest test, String tag, String pattern,
 659                       String toMatch, boolean badPattern,
 660                       boolean shouldMatch, String[] parens)
 661     {
 662         this.number = ++test.testCount;
 663         this.test = test;
 664         this.tag = tag;
 665         this.pattern = pattern;
 666         this.toMatch = toMatch;
 667         this.badPattern = badPattern;
 668         this.shouldMatch = shouldMatch;
 669         if (parens != null) {
 670             this.parens = new String[parens.length];
 671             for (int i = 0; i < parens.length; i++) {
 672                 this.parens[i] = parens[i];
 673             }
 674         } else {
 675             this.parens = null;
 676         }
 677     }
 678 
 679     public void runTest()
 680     {
 681         test.say(tag + "(" + number + "): " + pattern);
 682         if (testCreation()) {
 683             testMatch();
 684         }
 685     }
 686 
 687     boolean testCreation()
 688     {
 689         try
 690         {
 691             // Compile it
 692             regexp = new RE();
 693             regexp.setProgram(test.compiler.compile(pattern));
 694             // Expression didn't cause an expected error
 695             if (badPattern)
 696             {
 697                 test.fail(log, "Was expected to be an error, but wasn't.");
 698                 return false;
 699             }
 700 
 701             return true;
 702         }
 703         // Some expressions *should* cause exceptions to be thrown
 704         catch (Exception e)
 705         {
 706             // If it was supposed to be an error, report success and continue
 707             if (badPattern)
 708             {
 709                 log.append("   Match: ERR\n");
 710                 success("Produces an error (" + e.toString() + "), as expected.");
 711                 return false;
 712             }
 713 
 714             // Wasn't supposed to be an error
 715             String message = (e.getMessage() == null) ? e.toString() : e.getMessage();
 716             test.fail(log, "Produces an unexpected exception \"" + message + "\"");
 717             e.printStackTrace();
 718         }
 719         catch (Error e)
 720         {
 721             // Internal error happened
 722             test.fail(log, "Compiler threw fatal error \"" + e.getMessage() + "\"");
 723             e.printStackTrace();
 724         }
 725 
 726         return false;
 727     }
 728 
 729     private void testMatch()
 730     {
 731         log.append("   Match against: '" + toMatch + "'\n");
 732         // Try regular matching
 733         try
 734         {
 735             // Match against the string
 736             boolean result = regexp.match(toMatch);
 737             log.append("   Matched: " + (result ? "YES" : "NO") + "\n");
 738 
 739             // Check result, parens, and iterators
 740             if (checkResult(result) && (!shouldMatch || checkParens()))
 741             {
 742                 // test match(CharacterIterator, int)
 743                 // for every CharacterIterator implementation.
 744                 log.append("   Match using StringCharacterIterator\n");
 745                 if (!tryMatchUsingCI(new StringCharacterIterator(toMatch)))
 746                     return;
 747 
 748                 log.append("   Match using CharacterArrayCharacterIterator\n");
 749                 if (!tryMatchUsingCI(new CharacterArrayCharacterIterator(toMatch.toCharArray(), 0, toMatch.length())))
 750                     return;
 751 
 752                 log.append("   Match using StreamCharacterIterator\n");
 753                 if (!tryMatchUsingCI(new StreamCharacterIterator(new StringBufferInputStream(toMatch))))
 754                     return;
 755 
 756                 log.append("   Match using ReaderCharacterIterator\n");
 757                 if (!tryMatchUsingCI(new ReaderCharacterIterator(new StringReader(toMatch))))
 758                     return;
 759             }
 760         }
 761         // Matcher blew it
 762         catch(Exception e)
 763         {
 764             test.fail(log, "Matcher threw exception: " + e.toString());
 765             e.printStackTrace();
 766         }
 767         // Internal error
 768         catch(Error e)
 769         {
 770             test.fail(log, "Matcher threw fatal error \"" + e.getMessage() + "\"");
 771             e.printStackTrace();
 772         }
 773     }
 774 
 775     private boolean checkResult(boolean result)
 776     {
 777         // Write status
 778         if (result == shouldMatch) {
 779             success((shouldMatch ? "Matched" : "Did not match")
 780                     + " \"" + toMatch + "\", as expected:");
 781             return true;
 782         } else {
 783             if (shouldMatch) {
 784                 test.fail(log, "Did not match \"" + toMatch + "\", when expected to.");
 785             } else {
 786                 test.fail(log, "Matched \"" + toMatch + "\", when not expected to.");
 787             }
 788             return false;
 789         }
 790     }
 791 
 792     private boolean checkParens()
 793     {
 794         // Show subexpression registers
 795         if (RETest.showSuccesses)
 796         {
 797             test.showParens(regexp);
 798         }
 799 
 800         log.append("   Paren count: " + regexp.getParenCount() + "\n");
 801         if (!assertEquals(log, "Wrong number of parens", parens.length, regexp.getParenCount()))
 802         {
 803             return false;
 804         }
 805 
 806         // Check registers against expected contents
 807         for (int p = 0; p < regexp.getParenCount(); p++)
 808         {
 809             log.append("   Paren " + p + ": " + regexp.getParen(p) + "\n");
 810 
 811             // Compare expected result with actual
 812             if ("null".equals(parens[p]) && regexp.getParen(p) == null)
 813             {
 814                 // Consider "null" in test file equal to null
 815                 continue;
 816             }
 817             if (!assertEquals(log, "Wrong register " + p, parens[p], regexp.getParen(p)))
 818             {
 819                 return false;
 820             }
 821         }
 822 
 823         return true;
 824     }
 825 
 826     boolean tryMatchUsingCI(CharacterIterator matchAgainst)
 827     {
 828         try {
 829             boolean result = regexp.match(matchAgainst, 0);
 830             log.append("   Match: " + (result ? "YES" : "NO") + "\n");
 831             return checkResult(result) && (!shouldMatch || checkParens());
 832         }
 833         // Matcher blew it
 834         catch(Exception e)
 835         {
 836             test.fail(log, "Matcher threw exception: " + e.toString());
 837             e.printStackTrace();
 838         }
 839         // Internal error
 840         catch(Error e)
 841         {
 842             test.fail(log, "Matcher threw fatal error \"" + e.getMessage() + "\"");
 843             e.printStackTrace();
 844         }
 845         return false;
 846     }
 847 
 848     public boolean assertEquals(StringBuffer log, String message, String expected, String actual)
 849     {
 850         if (expected != null && !expected.equals(actual)
 851             || actual != null && !actual.equals(expected))
 852         {
 853             test.fail(log, message + " (expected \"" + expected
 854                       + "\", actual \"" + actual + "\")");
 855             return false;
 856         }
 857         return true;
 858     }
 859 
 860     public boolean assertEquals(StringBuffer log, String message, int expected, int actual)
 861     {
 862         if (expected != actual) {
 863             test.fail(log, message + " (expected \"" + expected
 864                       + "\", actual \"" + actual + "\")");
 865             return false;
 866         }
 867         return true;
 868     }
 869 
 870     /**
 871      * Show a success
 872      * @param s Success story
 873      */
 874     void success(String s)
 875     {
 876         if (RETest.showSuccesses)
 877         {
 878             test.say("" + RETest.NEW_LINE + "-----------------------" + RETest.NEW_LINE + "");
 879             test.say("Expression #" + (number) + " \"" + pattern + "\" ");
 880             test.say("Success: " + s);
 881         }
 882     }
 883 }