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