1. package com.sun.org.apache.regexp.internal;
  2. /*
  3. * ====================================================================
  4. *
  5. * The Apache Software License, Version 1.1
  6. *
  7. * Copyright (c) 1999 The Apache Software Foundation. All rights
  8. * reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * 1. Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. *
  17. * 2. Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in
  19. * the documentation and/or other materials provided with the
  20. * distribution.
  21. *
  22. * 3. The end-user documentation included with the redistribution, if
  23. * any, must include the following acknowlegement:
  24. * "This product includes software developed by the
  25. * Apache Software Foundation (http://www.apache.org/)."
  26. * Alternately, this acknowlegement may appear in the software itself,
  27. * if and wherever such third-party acknowlegements normally appear.
  28. *
  29. * 4. The names "The Jakarta Project", "Jakarta-Regexp", and "Apache Software
  30. * Foundation" must not be used to endorse or promote products derived
  31. * from this software without prior written permission. For written
  32. * permission, please contact apache@apache.org.
  33. *
  34. * 5. Products derived from this software may not be called "Apache"
  35. * nor may "Apache" appear in their names without prior written
  36. * permission of the Apache Group.
  37. *
  38. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  39. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  40. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  41. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  42. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  43. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  44. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  45. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  46. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  47. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  48. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  49. * SUCH DAMAGE.
  50. * ====================================================================
  51. *
  52. * This software consists of voluntary contributions made by many
  53. * individuals on behalf of the Apache Software Foundation. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. *
  57. */
  58. import java.io.*;
  59. /**
  60. * Data driven (and optionally interactive) testing harness to exercise regular
  61. * expression compiler and matching engine.
  62. *
  63. * @author <a href="mailto:jonl@muppetlabs.com">Jonathan Locke</a>
  64. * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
  65. * @version $Id: RETest.java,v 1.2 2000/04/30 20:42:35 jon Exp $
  66. */
  67. public class RETest
  68. {
  69. // Construct a matcher and a debug compiler
  70. RE r = new RE();
  71. REDebugCompiler compiler = new REDebugCompiler();
  72. // True if we want to see output from success cases
  73. static final boolean showSuccesses = false;
  74. /**
  75. * Main program entrypoint. If an argument is given, it will be compiled
  76. * and interactive matching will ensue. If no argument is given, the
  77. * file RETest.txt will be used as automated testing input.
  78. * @param arg Command line arguments (optional regular expression)
  79. */
  80. public static void _main(String[] arg)
  81. {
  82. try
  83. {
  84. //new RETest(arg);
  85. test();
  86. }
  87. catch (Exception e)
  88. {
  89. e.printStackTrace();
  90. }
  91. }
  92. /**
  93. * Testing entrypoint.
  94. * @param arg Command line arguments
  95. * @exception Exception thrown in case of error
  96. */
  97. public static boolean test() throws Exception
  98. {
  99. RETest test = new RETest();
  100. test.runAutomatedTests("docs/RETest.txt");
  101. return test.failures == 0;
  102. }
  103. /**
  104. * Constructor
  105. */
  106. public RETest()
  107. {
  108. }
  109. /**
  110. * Constructor for test
  111. * @param arg Command line arguments
  112. */
  113. public RETest(String[] arg)
  114. {
  115. try
  116. {
  117. // Run interactive tests against a single regexp
  118. if (arg.length == 2)
  119. {
  120. runInteractiveTests(arg[1]);
  121. }
  122. else if (arg.length == 1)
  123. {
  124. // Run automated tests
  125. runAutomatedTests(arg[0]);
  126. }
  127. else
  128. {
  129. System.out.println ( "Usage: RETest ([-i] [regex]) ([/path/to/testfile.txt])" );
  130. }
  131. }
  132. catch (Exception e)
  133. {
  134. e.printStackTrace();
  135. }
  136. }
  137. /**
  138. * Compile and test matching against a single expression
  139. * @param expr Expression to compile and test
  140. */
  141. void runInteractiveTests(String expr)
  142. {
  143. try
  144. {
  145. // Compile expression
  146. r.setProgram(compiler.compile(expr));
  147. // Show expression
  148. say("\n" + expr + "\n");
  149. // Show program for compiled expression
  150. compiler.dumpProgram(new PrintWriter(System.out));
  151. // Test matching against compiled expression
  152. while (true)
  153. {
  154. // Read from keyboard
  155. BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  156. System.out.print("> ");
  157. System.out.flush();
  158. String match = br.readLine();
  159. // Try a match against the keyboard input
  160. if (r.match(match))
  161. {
  162. say("Match successful.");
  163. }
  164. else
  165. {
  166. say("Match failed.");
  167. }
  168. // Show subparen registers
  169. showParens(r);
  170. }
  171. }
  172. catch (Exception e)
  173. {
  174. say("Error: " + e.toString());
  175. e.printStackTrace();
  176. }
  177. }
  178. /**
  179. * Exit with a fatal error.
  180. * @param s Last famous words before exiting
  181. */
  182. void die(String s)
  183. {
  184. say("FATAL ERROR: " + s);
  185. System.exit(0);
  186. }
  187. /**
  188. * Fail with an error
  189. * @param s Failure description
  190. */
  191. void fail(String s)
  192. {
  193. failures++;
  194. say("\n");
  195. say("*******************************************************");
  196. say("********************* FAILURE! **********************");
  197. say("*******************************************************");
  198. say("\n");
  199. say(s);
  200. say("");
  201. compiler.dumpProgram(new PrintWriter(System.out));
  202. say("\n");
  203. }
  204. /**
  205. * Show a success
  206. * @param s Success story
  207. */
  208. void success(String s)
  209. {
  210. if (showSuccesses)
  211. {
  212. show();
  213. say("Success: " + s);
  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. * Show an expression
  226. */
  227. void show()
  228. {
  229. say("\n-----------------------\n");
  230. say("Expression #" + (n) + " \"" + expr + "\" ");
  231. }
  232. /**
  233. * Dump parenthesized subexpressions found by a regular expression matcher object
  234. * @param r Matcher object with results to show
  235. */
  236. void showParens(RE r)
  237. {
  238. // Loop through each paren
  239. for (int i = 0; i < r.getParenCount(); i++)
  240. {
  241. // Show paren register
  242. say("$" + i + " = " + r.getParen(i));
  243. }
  244. }
  245. // Pre-compiled regular expression "a*b"
  246. char[] re1Instructions =
  247. {
  248. 0x007c, 0x0000, 0x001a, 0x007c, 0x0000, 0x000d, 0x0041,
  249. 0x0001, 0x0004, 0x0061, 0x007c, 0x0000, 0x0003, 0x0047,
  250. 0x0000, 0xfff6, 0x007c, 0x0000, 0x0003, 0x004e, 0x0000,
  251. 0x0003, 0x0041, 0x0001, 0x0004, 0x0062, 0x0045, 0x0000,
  252. 0x0000,
  253. };
  254. REProgram re1 = new REProgram(re1Instructions);
  255. /*
  256. * Current expression and number in automated test
  257. */
  258. String expr;
  259. int n = 0;
  260. /*
  261. * Count of failures in automated test
  262. */
  263. int failures = 0;
  264. /**
  265. * Run automated tests in RETest.txt file (from Perl 4.0 test battery)
  266. * @exception Exception thrown in case of error
  267. */
  268. void runAutomatedTests(String testDocument) throws Exception
  269. {
  270. long ms = System.currentTimeMillis();
  271. // Simple test of pre-compiled regular expressions
  272. RE r = new RE(re1);
  273. say("a*b");
  274. say("aaaab = " + r.match("aaab"));
  275. showParens(r);
  276. say("b = " + r.match("b"));
  277. showParens(r);
  278. say("c = " + r.match("c"));
  279. showParens(r);
  280. say("ccccaaaaab = " + r.match("ccccaaaaab"));
  281. showParens(r);
  282. r = new RE("a*b");
  283. String[] s = r.split("xxxxaabxxxxbyyyyaaabzzz");
  284. r = new RE("x+");
  285. s = r.grep(s);
  286. for (int i = 0; i < s.length; i++)
  287. {
  288. System.out.println ("s[" + i + "] = " + s[i]);
  289. }
  290. r = new RE("a*b");
  291. String s1 = r.subst("aaaabfooaaabgarplyaaabwackyb", "-");
  292. System.out.println ("s = " + s1);
  293. // Test from script file
  294. File testInput = new File(testDocument);
  295. if (! testInput.exists())
  296. throw new Exception ("Could not find: " + testDocument);
  297. BufferedReader br = new BufferedReader(new FileReader(testInput));
  298. try
  299. {
  300. // While input is available, parse lines
  301. while (br.ready())
  302. {
  303. // Find next re test case
  304. String number = "";
  305. String yesno;
  306. while (br.ready())
  307. {
  308. number = br.readLine();
  309. if (number == null)
  310. {
  311. break;
  312. }
  313. number = number.trim();
  314. if (number.startsWith("#"))
  315. {
  316. break;
  317. }
  318. if (!number.equals(""))
  319. {
  320. System.out.println ("Script error. Line = " + number);
  321. System.exit(0);
  322. }
  323. }
  324. // Are we done?
  325. if (!br.ready())
  326. {
  327. break;
  328. }
  329. // Get expression
  330. expr = br.readLine();
  331. n++;
  332. say("");
  333. say(n + ". " + expr);
  334. say("");
  335. // Compile it
  336. try
  337. {
  338. r.setProgram(compiler.compile(expr));
  339. }
  340. // Some expressions *should* cause exceptions to be thrown
  341. catch (Exception e)
  342. {
  343. // Get expected result
  344. yesno = br.readLine().trim();
  345. // If it was supposed to be an error, report success and continue
  346. if (yesno.equals("ERR"))
  347. {
  348. say(" Match: ERR");
  349. success("Produces an error (" + e.toString() + "), as expected.");
  350. continue;
  351. }
  352. // Wasn't supposed to be an error
  353. fail("Produces the unexpected error \"" + e.getMessage() + "\"");
  354. }
  355. catch (Error e)
  356. {
  357. // Internal error happened
  358. fail("Compiler threw fatal error \"" + e.getMessage() + "\"");
  359. e.printStackTrace();
  360. }
  361. // Get string to match against
  362. String matchAgainst = br.readLine().trim();
  363. say(" Match against: '" + matchAgainst + "'");
  364. // Expression didn't cause an expected error
  365. if (matchAgainst.equals("ERR"))
  366. {
  367. fail("Was expected to be an error, but wasn't.");
  368. continue;
  369. }
  370. // Try matching
  371. try
  372. {
  373. // Match against the string
  374. boolean b = r.match(matchAgainst);
  375. // Get expected result
  376. yesno = br.readLine().trim();
  377. // If match succeeded
  378. if (b)
  379. {
  380. // Status
  381. say(" Match: YES");
  382. // Match wasn't supposed to succeed
  383. if (yesno.equals("NO"))
  384. {
  385. fail("Matched \"" + matchAgainst + "\", when not expected to.");
  386. }
  387. else
  388. if (yesno.equals("YES"))
  389. {
  390. // Match succeeded as expected
  391. success("Matched \"" + matchAgainst + "\", as expected:");
  392. // Show subexpression registers
  393. if (showSuccesses)
  394. {
  395. showParens(r);
  396. }
  397. say(" Paren count: " + r.getParenCount());
  398. // Check registers against expected contents
  399. for (int p = 0; p < r.getParenCount(); p++)
  400. {
  401. // Get next register
  402. String register = br.readLine().trim();
  403. say(" Paren " + p + " : " + r.getParen(p));
  404. // Compare expected result with actual
  405. if (!register.equals(r.getParen(p)))
  406. {
  407. // Register isn't what it was supposed to be
  408. fail("Register " + p + " should be = \"" + register + "\", but is \"" + r.getParen(p) + "\" instead.");
  409. }
  410. }
  411. }
  412. else
  413. {
  414. // Bad test script
  415. die("Test script error!");
  416. }
  417. }
  418. else
  419. {
  420. // Status
  421. say(" Match: NO");
  422. // Match failed
  423. if (yesno.equals("YES"))
  424. {
  425. // Should have failed
  426. fail("Did not match \"" + matchAgainst + "\", when expected to.");
  427. }
  428. else
  429. if (yesno.equals("NO"))
  430. {
  431. // Should have failed
  432. success("Did not match \"" + matchAgainst + "\", as expected.");
  433. }
  434. else
  435. {
  436. // Bad test script
  437. die("Test script error!");
  438. }
  439. }
  440. }
  441. // Matcher blew it
  442. catch (Exception e)
  443. {
  444. fail("Matcher threw exception: " + e.toString());
  445. e.printStackTrace();
  446. }
  447. // Internal error
  448. catch (Error e)
  449. {
  450. fail("Matcher threw fatal error \"" + e.getMessage() + "\"");
  451. e.printStackTrace();
  452. }
  453. }
  454. }
  455. finally
  456. {
  457. br.close();
  458. }
  459. // Show match time
  460. System.out.println ("\n\nMatch time = " + (System.currentTimeMillis() - ms) + " ms.");
  461. // Print final results
  462. System.out.println ("\nTests complete. " + n + " tests, " + failures + " failure(s).");
  463. }
  464. }