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 } |
|