001: package org.apache.regexp;
002:
003: /*
004: * ====================================================================
005: *
006: * The Apache Software License, Version 1.1
007: *
008: * Copyright (c) 1999 The Apache Software Foundation. All rights
009: * reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions
013: * are met:
014: *
015: * 1. Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * 2. Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in
020: * the documentation and/or other materials provided with the
021: * distribution.
022: *
023: * 3. The end-user documentation included with the redistribution, if
024: * any, must include the following acknowlegement:
025: * "This product includes software developed by the
026: * Apache Software Foundation (http://www.apache.org/)."
027: * Alternately, this acknowlegement may appear in the software itself,
028: * if and wherever such third-party acknowlegements normally appear.
029: *
030: * 4. The names "The Jakarta Project", "Jakarta-Regexp", and "Apache Software
031: * Foundation" must not be used to endorse or promote products derived
032: * from this software without prior written permission. For written
033: * permission, please contact apache@apache.org.
034: *
035: * 5. Products derived from this software may not be called "Apache"
036: * nor may "Apache" appear in their names without prior written
037: * permission of the Apache Group.
038: *
039: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
040: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
041: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
042: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
043: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
044: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
045: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
046: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
047: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
048: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
049: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
050: * SUCH DAMAGE.
051: * ====================================================================
052: *
053: * This software consists of voluntary contributions made by many
054: * individuals on behalf of the Apache Software Foundation. For more
055: * information on the Apache Software Foundation, please see
056: * <http://www.apache.org/>.
057: *
058: */
059:
060: import java.io.*;
061:
062: /**
063: * Data driven (and optionally interactive) testing harness to exercise regular
064: * expression compiler and matching engine.
065: *
066: * @author <a href="mailto:jonl@muppetlabs.com">Jonathan Locke</a>
067: * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
068: * @version $Id: RETest.java,v 1.1.1.1 2002/01/31 03:14:36 rcm Exp $
069: */
070: public class RETest {
071: // Construct a matcher and a debug compiler
072: RE r = new RE();
073: REDebugCompiler compiler = new REDebugCompiler();
074:
075: // True if we want to see output from success cases
076: static final boolean showSuccesses = false;
077:
078: /**
079: * Main program entrypoint. If an argument is given, it will be compiled
080: * and interactive matching will ensue. If no argument is given, the
081: * file RETest.txt will be used as automated testing input.
082: * @param arg Command line arguments (optional regular expression)
083: */
084: public static void main(String[] arg) {
085: try {
086: //new RETest(arg);
087: test();
088: } catch (Exception e) {
089: e.printStackTrace();
090: }
091: }
092:
093: /**
094: * Testing entrypoint.
095: * @param arg Command line arguments
096: * @exception Exception thrown in case of error
097: */
098: public static boolean test() throws Exception {
099: RETest test = new RETest();
100: test.runAutomatedTests("docs/RETest.txt");
101: return test.failures == 0;
102: }
103:
104: /**
105: * Constructor
106: */
107: public RETest() {
108: }
109:
110: /**
111: * Constructor for test
112: * @param arg Command line arguments
113: */
114: public RETest(String[] arg) {
115: try {
116: // Run interactive tests against a single regexp
117: if (arg.length == 2) {
118: runInteractiveTests(arg[1]);
119: } else if (arg.length == 1) {
120: // Run automated tests
121: runAutomatedTests(arg[0]);
122: } else {
123: System.out
124: .println("Usage: RETest ([-i] [regex]) ([/path/to/testfile.txt])");
125: }
126: } catch (Exception e) {
127: e.printStackTrace();
128: }
129: }
130:
131: /**
132: * Compile and test matching against a single expression
133: * @param expr Expression to compile and test
134: */
135: void runInteractiveTests(String expr) {
136: try {
137: // Compile expression
138: r.setProgram(compiler.compile(expr));
139:
140: // Show expression
141: say("\n" + expr + "\n");
142:
143: // Show program for compiled expression
144: compiler.dumpProgram(new PrintWriter(System.out));
145:
146: // Test matching against compiled expression
147: while (true) {
148: // Read from keyboard
149: BufferedReader br = new BufferedReader(
150: new InputStreamReader(System.in));
151: System.out.print("> ");
152: System.out.flush();
153: String match = br.readLine();
154:
155: // Try a match against the keyboard input
156: if (r.match(match)) {
157: say("Match successful.");
158: } else {
159: say("Match failed.");
160: }
161:
162: // Show subparen registers
163: showParens(r);
164: }
165: } catch (Exception e) {
166: say("Error: " + e.toString());
167: e.printStackTrace();
168: }
169: }
170:
171: /**
172: * Exit with a fatal error.
173: * @param s Last famous words before exiting
174: */
175: void die(String s) {
176: say("FATAL ERROR: " + s);
177: System.exit(0);
178: }
179:
180: /**
181: * Fail with an error
182: * @param s Failure description
183: */
184: void fail(String s) {
185: failures++;
186: say("\n");
187: say("*******************************************************");
188: say("********************* FAILURE! **********************");
189: say("*******************************************************");
190: say("\n");
191: say(s);
192: say("");
193: compiler.dumpProgram(new PrintWriter(System.out));
194: say("\n");
195: }
196:
197: /**
198: * Show a success
199: * @param s Success story
200: */
201: void success(String s) {
202: if (showSuccesses) {
203: show();
204: say("Success: " + s);
205: }
206: }
207:
208: /**
209: * Say something to standard out
210: * @param s What to say
211: */
212: void say(String s) {
213: System.out.println(s);
214: }
215:
216: /**
217: * Show an expression
218: */
219: void show() {
220: say("\n-----------------------\n");
221: say("Expression #" + (n) + " \"" + expr + "\" ");
222: }
223:
224: /**
225: * Dump parenthesized subexpressions found by a regular expression matcher object
226: * @param r Matcher object with results to show
227: */
228: void showParens(RE r) {
229: // Loop through each paren
230: for (int i = 0; i < r.getParenCount(); i++) {
231: // Show paren register
232: say("$" + i + " = " + r.getParen(i));
233: }
234: }
235:
236: // Pre-compiled regular expression "a*b"
237: char[] re1Instructions = { 0x007c, 0x0000, 0x001a, 0x007c, 0x0000,
238: 0x000d, 0x0041, 0x0001, 0x0004, 0x0061, 0x007c, 0x0000,
239: 0x0003, 0x0047, 0x0000, 0xfff6, 0x007c, 0x0000, 0x0003,
240: 0x004e, 0x0000, 0x0003, 0x0041, 0x0001, 0x0004, 0x0062,
241: 0x0045, 0x0000, 0x0000, };
242:
243: REProgram re1 = new REProgram(re1Instructions);
244:
245: /*
246: * Current expression and number in automated test
247: */
248: String expr;
249: int n = 0;
250:
251: /*
252: * Count of failures in automated test
253: */
254: int failures = 0;
255:
256: /**
257: * Run automated tests in RETest.txt file (from Perl 4.0 test battery)
258: * @exception Exception thrown in case of error
259: */
260: void runAutomatedTests(String testDocument) throws Exception {
261: long ms = System.currentTimeMillis();
262:
263: // Simple test of pre-compiled regular expressions
264: RE r = new RE(re1);
265: say("a*b");
266: say("aaaab = " + r.match("aaab"));
267: showParens(r);
268: say("b = " + r.match("b"));
269: showParens(r);
270: say("c = " + r.match("c"));
271: showParens(r);
272: say("ccccaaaaab = " + r.match("ccccaaaaab"));
273: showParens(r);
274:
275: r = new RE("a*b");
276: String[] s = r.split("xxxxaabxxxxbyyyyaaabzzz");
277: r = new RE("x+");
278: s = r.grep(s);
279: for (int i = 0; i < s.length; i++) {
280: System.out.println("s[" + i + "] = " + s[i]);
281: }
282:
283: r = new RE("a*b");
284: String s1 = r.subst("aaaabfooaaabgarplyaaabwackyb", "-");
285: System.out.println("s = " + s1);
286:
287: // Test from script file
288: File testInput = new File(testDocument);
289: if (!testInput.exists())
290: throw new Exception("Could not find: " + testDocument);
291: BufferedReader br = new BufferedReader(
292: new FileReader(testInput));
293: try {
294: // While input is available, parse lines
295: while (br.ready()) {
296: // Find next re test case
297: String number = "";
298: String yesno;
299: while (br.ready()) {
300: number = br.readLine();
301: if (number == null) {
302: break;
303: }
304: number = number.trim();
305: if (number.startsWith("#")) {
306: break;
307: }
308: if (!number.equals("")) {
309: System.out.println("Script error. Line = "
310: + number);
311: System.exit(0);
312: }
313: }
314:
315: // Are we done?
316: if (!br.ready()) {
317: break;
318: }
319:
320: // Get expression
321: expr = br.readLine();
322: n++;
323: say("");
324: say(n + ". " + expr);
325: say("");
326:
327: // Compile it
328: try {
329: r.setProgram(compiler.compile(expr));
330: }
331:
332: // Some expressions *should* cause exceptions to be thrown
333: catch (Exception e) {
334: // Get expected result
335: yesno = br.readLine().trim();
336:
337: // If it was supposed to be an error, report success and continue
338: if (yesno.equals("ERR")) {
339: say(" Match: ERR");
340: success("Produces an error (" + e.toString()
341: + "), as expected.");
342: continue;
343: }
344:
345: // Wasn't supposed to be an error
346: fail("Produces the unexpected error \""
347: + e.getMessage() + "\"");
348: } catch (Error e) {
349: // Internal error happened
350: fail("Compiler threw fatal error \""
351: + e.getMessage() + "\"");
352: e.printStackTrace();
353: }
354:
355: // Get string to match against
356: String matchAgainst = br.readLine().trim();
357: say(" Match against: '" + matchAgainst + "'");
358:
359: // Expression didn't cause an expected error
360: if (matchAgainst.equals("ERR")) {
361: fail("Was expected to be an error, but wasn't.");
362: continue;
363: }
364:
365: // Try matching
366: try {
367: // Match against the string
368: boolean b = r.match(matchAgainst);
369:
370: // Get expected result
371: yesno = br.readLine().trim();
372:
373: // If match succeeded
374: if (b) {
375: // Status
376: say(" Match: YES");
377:
378: // Match wasn't supposed to succeed
379: if (yesno.equals("NO")) {
380: fail("Matched \"" + matchAgainst
381: + "\", when not expected to.");
382: } else if (yesno.equals("YES")) {
383: // Match succeeded as expected
384: success("Matched \"" + matchAgainst
385: + "\", as expected:");
386:
387: // Show subexpression registers
388: if (showSuccesses) {
389: showParens(r);
390: }
391:
392: say(" Paren count: " + r.getParenCount());
393:
394: // Check registers against expected contents
395: for (int p = 0; p < r.getParenCount(); p++) {
396: // Get next register
397: String register = br.readLine().trim();
398: say(" Paren " + p + " : "
399: + r.getParen(p));
400:
401: // Compare expected result with actual
402: if (!register.equals(r.getParen(p))) {
403: // Register isn't what it was supposed to be
404: fail("Register " + p
405: + " should be = \""
406: + register
407: + "\", but is \""
408: + r.getParen(p)
409: + "\" instead.");
410: }
411: }
412: } else {
413: // Bad test script
414: die("Test script error!");
415: }
416: } else {
417: // Status
418: say(" Match: NO");
419:
420: // Match failed
421: if (yesno.equals("YES")) {
422: // Should have failed
423: fail("Did not match \"" + matchAgainst
424: + "\", when expected to.");
425: } else if (yesno.equals("NO")) {
426: // Should have failed
427: success("Did not match \"" + matchAgainst
428: + "\", as expected.");
429: } else {
430: // Bad test script
431: die("Test script error!");
432: }
433: }
434: }
435:
436: // Matcher blew it
437: catch (Exception e) {
438: fail("Matcher threw exception: " + e.toString());
439: e.printStackTrace();
440: }
441:
442: // Internal error
443: catch (Error e) {
444: fail("Matcher threw fatal error \""
445: + e.getMessage() + "\"");
446: e.printStackTrace();
447: }
448: }
449: } finally {
450: br.close();
451: }
452:
453: // Show match time
454: System.out.println("\n\nMatch time = "
455: + (System.currentTimeMillis() - ms) + " ms.");
456:
457: // Print final results
458: System.out.println("\nTests complete. " + n + " tests, "
459: + failures + " failure(s).");
460: }
461: }
|