1/* 2 [The "BSD licence"] 3 Copyright (c) 2007-2008 Leon Jen-Yuan Su 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 1. Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 3. The name of the author may not be used to endorse or promote products 15 derived from this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27*/ 28package org.antlr.gunit; 29 30import org.antlr.runtime.*; 31import org.antlr.runtime.tree.CommonTree; 32import org.antlr.runtime.tree.CommonTreeNodeStream; 33import org.antlr.runtime.tree.TreeAdaptor; 34import org.antlr.runtime.tree.TreeNodeStream; 35import org.antlr.stringtemplate.CommonGroupLoader; 36import org.antlr.stringtemplate.StringTemplate; 37import org.antlr.stringtemplate.StringTemplateGroup; 38import org.antlr.stringtemplate.StringTemplateGroupLoader; 39import org.antlr.stringtemplate.language.AngleBracketTemplateLexer; 40 41import java.io.ByteArrayOutputStream; 42import java.io.File; 43import java.io.IOException; 44import java.io.PrintStream; 45import java.lang.reflect.Constructor; 46import java.lang.reflect.InvocationTargetException; 47import java.lang.reflect.Method; 48import java.util.ArrayList; 49import java.util.List; 50 51public class gUnitExecutor implements ITestSuite { 52 public GrammarInfo grammarInfo; 53 54 private final ClassLoader grammarClassLoader; 55 56 private final String testsuiteDir; 57 58 public int numOfTest; 59 60 public int numOfSuccess; 61 62 public int numOfFailure; 63 64 private String title; 65 66 public int numOfInvalidInput; 67 68 private String parserName; 69 70 private String lexerName; 71 72 public List<AbstractTest> failures; 73 public List<AbstractTest> invalids; 74 75 private PrintStream console = System.out; 76 private PrintStream consoleErr = System.err; 77 78 public gUnitExecutor(GrammarInfo grammarInfo, String testsuiteDir) { 79 this( grammarInfo, determineClassLoader(), testsuiteDir); 80 } 81 82 private static ClassLoader determineClassLoader() { 83 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 84 if ( classLoader == null ) { 85 classLoader = gUnitExecutor.class.getClassLoader(); 86 } 87 return classLoader; 88 } 89 90 public gUnitExecutor(GrammarInfo grammarInfo, ClassLoader grammarClassLoader, String testsuiteDir) { 91 this.grammarInfo = grammarInfo; 92 this.grammarClassLoader = grammarClassLoader; 93 this.testsuiteDir = testsuiteDir; 94 numOfTest = 0; 95 numOfSuccess = 0; 96 numOfFailure = 0; 97 numOfInvalidInput = 0; 98 failures = new ArrayList<AbstractTest>(); 99 invalids = new ArrayList<AbstractTest>(); 100 } 101 102 protected ClassLoader getGrammarClassLoader() { 103 return grammarClassLoader; 104 } 105 106 protected final Class classForName(String name) throws ClassNotFoundException { 107 return getGrammarClassLoader().loadClass( name ); 108 } 109 110 public String execTest() throws IOException{ 111 // Set up string template for testing result 112 StringTemplate testResultST = getTemplateGroup().getInstanceOf("testResult"); 113 try { 114 /** Set up appropriate path for parser/lexer if using package */ 115 if (grammarInfo.getGrammarPackage()!=null ) { 116 parserName = grammarInfo.getGrammarPackage()+"."+grammarInfo.getGrammarName()+"Parser"; 117 lexerName = grammarInfo.getGrammarPackage()+"."+grammarInfo.getGrammarName()+"Lexer"; 118 } 119 else { 120 parserName = grammarInfo.getGrammarName()+"Parser"; 121 lexerName = grammarInfo.getGrammarName()+"Lexer"; 122 } 123 124 /*** Start Unit/Functional Testing ***/ 125 // Execute unit test of for parser, lexer and tree grammar 126 if ( grammarInfo.getTreeGrammarName()!=null ) { 127 title = "executing testsuite for tree grammar:"+grammarInfo.getTreeGrammarName()+" walks "+parserName; 128 } 129 else { 130 title = "executing testsuite for grammar:"+grammarInfo.getGrammarName(); 131 } 132 executeTests(); 133 // End of exection of unit testing 134 135 // Fill in the template holes with the test results 136 testResultST.setAttribute("title", title); 137 testResultST.setAttribute("num_of_test", numOfTest); 138 testResultST.setAttribute("num_of_failure", numOfFailure); 139 if ( numOfFailure>0 ) { 140 testResultST.setAttribute("failure", failures); 141 } 142 if ( numOfInvalidInput>0 ) { 143 testResultST.setAttribute("has_invalid", true); 144 testResultST.setAttribute("num_of_invalid", numOfInvalidInput); 145 testResultST.setAttribute("invalid", invalids); 146 } 147 } 148 catch (Exception e) { 149 e.printStackTrace(); 150 System.exit(1); 151 } 152 return testResultST.toString(); 153 } 154 155 private StringTemplateGroup getTemplateGroup() { 156 StringTemplateGroupLoader loader = new CommonGroupLoader("org/antlr/gunit", null); 157 StringTemplateGroup.registerGroupLoader(loader); 158 StringTemplateGroup.registerDefaultLexer(AngleBracketTemplateLexer.class); 159 StringTemplateGroup group = StringTemplateGroup.loadGroup("gUnitTestResult"); 160 return group; 161 } 162 163 // TODO: throw more specific exceptions 164 private gUnitTestResult runCorrectParser(String parserName, String lexerName, String rule, String lexicalRule, String treeRule, gUnitTestInput input) throws Exception 165 { 166 if ( lexicalRule!=null ) return runLexer(lexerName, lexicalRule, input); 167 else if ( treeRule!=null ) return runTreeParser(parserName, lexerName, rule, treeRule, input); 168 else return runParser(parserName, lexerName, rule, input); 169 } 170 171 private void executeTests() throws Exception { 172 for ( gUnitTestSuite ts: grammarInfo.getRuleTestSuites() ) { 173 String rule = ts.getRuleName(); 174 String lexicalRule = ts.getLexicalRuleName(); 175 String treeRule = ts.getTreeRuleName(); 176 for ( gUnitTestInput input: ts.testSuites.keySet() ) { // each rule may contain multiple tests 177 numOfTest++; 178 // Run parser, and get the return value or stdout or stderr if there is 179 gUnitTestResult result = null; 180 AbstractTest test = ts.testSuites.get(input); 181 try { 182 // TODO: create a -debug option to turn on logging, which shows progress of running tests 183 //System.out.print(numOfTest + ". Running rule: " + rule + "; input: '" + input.testInput + "'"); 184 result = runCorrectParser(parserName, lexerName, rule, lexicalRule, treeRule, input); 185 // TODO: create a -debug option to turn on logging, which shows progress of running tests 186 //System.out.println("; Expecting " + test.getExpected() + "; Success?: " + test.getExpected().equals(test.getResult(result))); 187 } catch ( InvalidInputException e) { 188 numOfInvalidInput++; 189 test.setHeader(rule, lexicalRule, treeRule, numOfTest, input.line); 190 test.setActual(input.input); 191 invalids.add(test); 192 continue; 193 } // TODO: ensure there's no other exceptions required to be handled here... 194 195 String expected = test.getExpected(); 196 String actual = test.getResult(result); 197 test.setActual(actual); 198 199 if (actual == null) { 200 numOfFailure++; 201 test.setHeader(rule, lexicalRule, treeRule, numOfTest, input.line); 202 test.setActual("null"); 203 failures.add(test); 204 onFail(test); 205 } 206 // the 2nd condition is used for the assertFAIL test of lexer rule because BooleanTest return err msg instead of 'FAIL' if isLexerTest 207 else if ( expected.equals(actual) || (expected.equals("FAIL")&&!actual.equals("OK") ) ) { 208 numOfSuccess++; 209 onPass(test); 210 } 211 // TODO: something with ACTIONS - at least create action test type and throw exception. 212 else if ( ts.testSuites.get(input).getType()==gUnitParser.ACTION ) { // expected Token: ACTION 213 numOfFailure++; 214 test.setHeader(rule, lexicalRule, treeRule, numOfTest, input.line); 215 test.setActual("\t"+"{ACTION} is not supported in the grammarInfo yet..."); 216 failures.add(test); 217 onFail(test); 218 } 219 else { 220 numOfFailure++; 221 test.setHeader(rule, lexicalRule, treeRule, numOfTest, input.line); 222 failures.add(test); 223 onFail(test); 224 } 225 } // end of 2nd for-loop: tests for individual rule 226 } // end of 1st for-loop: testsuites for grammar 227 } 228 229 // TODO: throw proper exceptions 230 protected gUnitTestResult runLexer(String lexerName, String testRuleName, gUnitTestInput testInput) throws Exception { 231 CharStream input; 232 Class lexer = null; 233 PrintStream ps = null; // for redirecting stdout later 234 PrintStream ps2 = null; // for redirecting stderr later 235 try { 236 /** Set up ANTLR input stream based on input source, file or String */ 237 input = getANTLRInputStream(testInput); 238 239 /** Use Reflection to create instances of lexer and parser */ 240 lexer = classForName(lexerName); 241 Class[] lexArgTypes = new Class[]{CharStream.class}; // assign type to lexer's args 242 Constructor lexConstructor = lexer.getConstructor(lexArgTypes); 243 Object[] lexArgs = new Object[]{input}; // assign value to lexer's args 244 Object lexObj = lexConstructor.newInstance(lexArgs); // makes new instance of lexer 245 246 Method ruleName = lexer.getMethod("m"+testRuleName, new Class[0]); 247 248 /** Start of I/O Redirecting */ 249 ByteArrayOutputStream out = new ByteArrayOutputStream(); 250 ByteArrayOutputStream err = new ByteArrayOutputStream(); 251 ps = new PrintStream(out); 252 ps2 = new PrintStream(err); 253 System.setOut(ps); 254 System.setErr(ps2); 255 /** End of redirecting */ 256 257 /** Invoke lexer rule, and get the current index in CharStream */ 258 ruleName.invoke(lexObj, new Object[0]); 259 Method ruleName2 = lexer.getMethod("getCharIndex", new Class[0]); 260 int currentIndex = (Integer) ruleName2.invoke(lexObj, new Object[0]); 261 if ( currentIndex!=input.size() ) { 262 ps2.print("extra text found, '"+input.substring(currentIndex, input.size()-1)+"'"); 263 } 264 265 if ( err.toString().length()>0 ) { 266 gUnitTestResult testResult = new gUnitTestResult(false, err.toString(), true); 267 testResult.setError(err.toString()); 268 return testResult; 269 } 270 String stdout = null; 271 if ( out.toString().length()>0 ) { 272 stdout = out.toString(); 273 } 274 return new gUnitTestResult(true, stdout, true); 275 } catch (IOException e) { 276 return getTestExceptionResult(e); 277 } catch (ClassNotFoundException e) { 278 e.printStackTrace(); System.exit(1); 279 } catch (SecurityException e) { 280 e.printStackTrace(); System.exit(1); 281 } catch (NoSuchMethodException e) { 282 e.printStackTrace(); System.exit(1); 283 } catch (IllegalArgumentException e) { 284 e.printStackTrace(); System.exit(1); 285 } catch (InstantiationException e) { 286 e.printStackTrace(); System.exit(1); 287 } catch (IllegalAccessException e) { 288 e.printStackTrace(); System.exit(1); 289 } catch (InvocationTargetException e) { // This exception could be caused from ANTLR Runtime Exception, e.g. MismatchedTokenException 290 return getTestExceptionResult(e); 291 } finally { 292 try { 293 if ( ps!=null ) ps.close(); 294 if ( ps2!=null ) ps2.close(); 295 System.setOut(console); // Reset standard output 296 System.setErr(consoleErr); // Reset standard err out 297 } catch (Exception e) { 298 e.printStackTrace(); 299 } 300 } 301 // TODO: verify this: 302 throw new Exception("This should be unreachable?"); 303 } 304 305 // TODO: throw proper exceptions 306 protected gUnitTestResult runParser(String parserName, String lexerName, String testRuleName, gUnitTestInput testInput) throws Exception { 307 CharStream input; 308 Class lexer = null; 309 Class parser = null; 310 PrintStream ps = null; // for redirecting stdout later 311 PrintStream ps2 = null; // for redirecting stderr later 312 try { 313 /** Set up ANTLR input stream based on input source, file or String */ 314 input = getANTLRInputStream(testInput); 315 316 /** Use Reflection to create instances of lexer and parser */ 317 lexer = classForName(lexerName); 318 Class[] lexArgTypes = new Class[]{CharStream.class}; // assign type to lexer's args 319 Constructor lexConstructor = lexer.getConstructor(lexArgTypes); 320 Object[] lexArgs = new Object[]{input}; // assign value to lexer's args 321 Object lexObj = lexConstructor.newInstance(lexArgs); // makes new instance of lexer 322 323 CommonTokenStream tokens = new CommonTokenStream((Lexer) lexObj); 324 325 parser = classForName(parserName); 326 Class[] parArgTypes = new Class[]{TokenStream.class}; // assign type to parser's args 327 Constructor parConstructor = parser.getConstructor(parArgTypes); 328 Object[] parArgs = new Object[]{tokens}; // assign value to parser's args 329 Object parObj = parConstructor.newInstance(parArgs); // makes new instance of parser 330 331 // set up customized tree adaptor if necessary 332 if ( grammarInfo.getAdaptor()!=null ) { 333 parArgTypes = new Class[]{TreeAdaptor.class}; 334 Method _setTreeAdaptor = parser.getMethod("setTreeAdaptor", parArgTypes); 335 Class _treeAdaptor = classForName(grammarInfo.getAdaptor()); 336 _setTreeAdaptor.invoke(parObj, _treeAdaptor.newInstance()); 337 } 338 339 Method ruleName = parser.getMethod(testRuleName); 340 341 /** Start of I/O Redirecting */ 342 ByteArrayOutputStream out = new ByteArrayOutputStream(); 343 ByteArrayOutputStream err = new ByteArrayOutputStream(); 344 ps = new PrintStream(out); 345 ps2 = new PrintStream(err); 346 System.setOut(ps); 347 System.setErr(ps2); 348 /** End of redirecting */ 349 350 /** Invoke grammar rule, and store if there is a return value */ 351 Object ruleReturn = ruleName.invoke(parObj); 352 String astString = null; 353 String stString = null; 354 /** If rule has return value, determine if it contains an AST or a ST */ 355 if ( ruleReturn!=null ) { 356 if ( ruleReturn.getClass().toString().indexOf(testRuleName+"_return")>0 ) { 357 try { // NullPointerException may happen here... 358 Class _return = classForName(parserName+"$"+testRuleName+"_return"); 359 Method[] methods = _return.getDeclaredMethods(); 360 for(Method method : methods) { 361 if ( method.getName().equals("getTree") ) { 362 Method returnName = _return.getMethod("getTree"); 363 CommonTree tree = (CommonTree) returnName.invoke(ruleReturn); 364 astString = tree.toStringTree(); 365 } 366 else if ( method.getName().equals("getTemplate") ) { 367 Method returnName = _return.getMethod("getTemplate"); 368 StringTemplate st = (StringTemplate) returnName.invoke(ruleReturn); 369 stString = st.toString(); 370 } 371 } 372 } 373 catch(Exception e) { 374 System.err.println(e); // Note: If any exception occurs, the test is viewed as failed. 375 } 376 } 377 } 378 379 /** Invalid input */ 380 if ( tokens.index()!=tokens.size()-1 ) { 381 //throw new InvalidInputException(); 382 ps2.print("Invalid input"); 383 } 384 385 if ( err.toString().length()>0 ) { 386 gUnitTestResult testResult = new gUnitTestResult(false, err.toString()); 387 testResult.setError(err.toString()); 388 return testResult; 389 } 390 String stdout = null; 391 // TODO: need to deal with the case which has both ST return value and stdout 392 if ( out.toString().length()>0 ) { 393 stdout = out.toString(); 394 } 395 if ( astString!=null ) { // Return toStringTree of AST 396 return new gUnitTestResult(true, stdout, astString); 397 } 398 else if ( stString!=null ) {// Return toString of ST 399 return new gUnitTestResult(true, stdout, stString); 400 } 401 402 if ( ruleReturn!=null ) { 403 // TODO: currently only works for a single return with int or String value 404 return new gUnitTestResult(true, stdout, String.valueOf(ruleReturn)); 405 } 406 return new gUnitTestResult(true, stdout, stdout); 407 } catch (IOException e) { 408 return getTestExceptionResult(e); 409 } catch (ClassNotFoundException e) { 410 e.printStackTrace(); System.exit(1); 411 } catch (SecurityException e) { 412 e.printStackTrace(); System.exit(1); 413 } catch (NoSuchMethodException e) { 414 e.printStackTrace(); System.exit(1); 415 } catch (IllegalArgumentException e) { 416 e.printStackTrace(); System.exit(1); 417 } catch (InstantiationException e) { 418 e.printStackTrace(); System.exit(1); 419 } catch (IllegalAccessException e) { 420 e.printStackTrace(); System.exit(1); 421 } catch (InvocationTargetException e) { // This exception could be caused from ANTLR Runtime Exception, e.g. MismatchedTokenException 422 return getTestExceptionResult(e); 423 } finally { 424 try { 425 if ( ps!=null ) ps.close(); 426 if ( ps2!=null ) ps2.close(); 427 System.setOut(console); // Reset standard output 428 System.setErr(consoleErr); // Reset standard err out 429 } catch (Exception e) { 430 e.printStackTrace(); 431 } 432 } 433 // TODO: verify this: 434 throw new Exception("This should be unreachable?"); 435 } 436 437 protected gUnitTestResult runTreeParser(String parserName, String lexerName, String testRuleName, String testTreeRuleName, gUnitTestInput testInput) throws Exception { 438 CharStream input; 439 String treeParserPath; 440 Class lexer = null; 441 Class parser = null; 442 Class treeParser = null; 443 PrintStream ps = null; // for redirecting stdout later 444 PrintStream ps2 = null; // for redirecting stderr later 445 try { 446 /** Set up ANTLR input stream based on input source, file or String */ 447 input = getANTLRInputStream(testInput); 448 449 /** Set up appropriate path for tree parser if using package */ 450 if ( grammarInfo.getGrammarPackage()!=null ) { 451 treeParserPath = grammarInfo.getGrammarPackage()+"."+grammarInfo.getTreeGrammarName(); 452 } 453 else { 454 treeParserPath = grammarInfo.getTreeGrammarName(); 455 } 456 457 /** Use Reflection to create instances of lexer and parser */ 458 lexer = classForName(lexerName); 459 Class[] lexArgTypes = new Class[]{CharStream.class}; // assign type to lexer's args 460 Constructor lexConstructor = lexer.getConstructor(lexArgTypes); 461 Object[] lexArgs = new Object[]{input}; // assign value to lexer's args 462 Object lexObj = lexConstructor.newInstance(lexArgs); // makes new instance of lexer 463 464 CommonTokenStream tokens = new CommonTokenStream((Lexer) lexObj); 465 466 parser = classForName(parserName); 467 Class[] parArgTypes = new Class[]{TokenStream.class}; // assign type to parser's args 468 Constructor parConstructor = parser.getConstructor(parArgTypes); 469 Object[] parArgs = new Object[]{tokens}; // assign value to parser's args 470 Object parObj = parConstructor.newInstance(parArgs); // makes new instance of parser 471 472 // set up customized tree adaptor if necessary 473 TreeAdaptor customTreeAdaptor = null; 474 if ( grammarInfo.getAdaptor()!=null ) { 475 parArgTypes = new Class[]{TreeAdaptor.class}; 476 Method _setTreeAdaptor = parser.getMethod("setTreeAdaptor", parArgTypes); 477 Class _treeAdaptor = classForName(grammarInfo.getAdaptor()); 478 customTreeAdaptor = (TreeAdaptor) _treeAdaptor.newInstance(); 479 _setTreeAdaptor.invoke(parObj, customTreeAdaptor); 480 } 481 482 Method ruleName = parser.getMethod(testRuleName); 483 484 /** Start of I/O Redirecting */ 485 ByteArrayOutputStream out = new ByteArrayOutputStream(); 486 ByteArrayOutputStream err = new ByteArrayOutputStream(); 487 ps = new PrintStream(out); 488 ps2 = new PrintStream(err); 489 System.setOut(ps); 490 System.setErr(ps2); 491 /** End of redirecting */ 492 493 /** Invoke grammar rule, and get the return value */ 494 Object ruleReturn = ruleName.invoke(parObj); 495 496 Class _return = classForName(parserName+"$"+testRuleName+"_return"); 497 Method returnName = _return.getMethod("getTree"); 498 CommonTree tree = (CommonTree) returnName.invoke(ruleReturn); 499 500 // Walk resulting tree; create tree nodes stream first 501 CommonTreeNodeStream nodes; 502 if ( customTreeAdaptor!=null ) { 503 nodes = new CommonTreeNodeStream(customTreeAdaptor, tree); 504 } 505 else { 506 nodes = new CommonTreeNodeStream(tree); 507 } 508 // AST nodes have payload that point into token stream 509 nodes.setTokenStream(tokens); 510 // Create a tree walker attached to the nodes stream 511 treeParser = classForName(treeParserPath); 512 Class[] treeParArgTypes = new Class[]{TreeNodeStream.class}; // assign type to tree parser's args 513 Constructor treeParConstructor = treeParser.getConstructor(treeParArgTypes); 514 Object[] treeParArgs = new Object[]{nodes}; // assign value to tree parser's args 515 Object treeParObj = treeParConstructor.newInstance(treeParArgs); // makes new instance of tree parser 516 // Invoke the tree rule, and store the return value if there is 517 Method treeRuleName = treeParser.getMethod(testTreeRuleName); 518 Object treeRuleReturn = treeRuleName.invoke(treeParObj); 519 520 String astString = null; 521 String stString = null; 522 /** If tree rule has return value, determine if it contains an AST or a ST */ 523 if ( treeRuleReturn!=null ) { 524 if ( treeRuleReturn.getClass().toString().indexOf(testTreeRuleName+"_return")>0 ) { 525 try { // NullPointerException may happen here... 526 Class _treeReturn = classForName(treeParserPath+"$"+testTreeRuleName+"_return"); 527 Method[] methods = _treeReturn.getDeclaredMethods(); 528 for(Method method : methods) { 529 if ( method.getName().equals("getTree") ) { 530 Method treeReturnName = _treeReturn.getMethod("getTree"); 531 CommonTree returnTree = (CommonTree) treeReturnName.invoke(treeRuleReturn); 532 astString = returnTree.toStringTree(); 533 } 534 else if ( method.getName().equals("getTemplate") ) { 535 Method treeReturnName = _return.getMethod("getTemplate"); 536 StringTemplate st = (StringTemplate) treeReturnName.invoke(treeRuleReturn); 537 stString = st.toString(); 538 } 539 } 540 } 541 catch(Exception e) { 542 System.err.println(e); // Note: If any exception occurs, the test is viewed as failed. 543 } 544 } 545 } 546 547 /** Invalid input */ 548 if ( tokens.index()!=tokens.size()-1 ) { 549 //throw new InvalidInputException(); 550 ps2.print("Invalid input"); 551 } 552 553 if ( err.toString().length()>0 ) { 554 gUnitTestResult testResult = new gUnitTestResult(false, err.toString()); 555 testResult.setError(err.toString()); 556 return testResult; 557 } 558 559 String stdout = null; 560 // TODO: need to deal with the case which has both ST return value and stdout 561 if ( out.toString().length()>0 ) { 562 stdout = out.toString(); 563 } 564 if ( astString!=null ) { // Return toStringTree of AST 565 return new gUnitTestResult(true, stdout, astString); 566 } 567 else if ( stString!=null ) {// Return toString of ST 568 return new gUnitTestResult(true, stdout, stString); 569 } 570 571 if ( treeRuleReturn!=null ) { 572 // TODO: again, currently only works for a single return with int or String value 573 return new gUnitTestResult(true, stdout, String.valueOf(treeRuleReturn)); 574 } 575 return new gUnitTestResult(true, stdout, stdout); 576 } catch (IOException e) { 577 return getTestExceptionResult(e); 578 } catch (ClassNotFoundException e) { 579 e.printStackTrace(); System.exit(1); 580 } catch (SecurityException e) { 581 e.printStackTrace(); System.exit(1); 582 } catch (NoSuchMethodException e) { 583 e.printStackTrace(); System.exit(1); 584 } catch (IllegalArgumentException e) { 585 e.printStackTrace(); System.exit(1); 586 } catch (InstantiationException e) { 587 e.printStackTrace(); System.exit(1); 588 } catch (IllegalAccessException e) { 589 e.printStackTrace(); System.exit(1); 590 } catch (InvocationTargetException e) { // note: This exception could be caused from ANTLR Runtime Exception... 591 return getTestExceptionResult(e); 592 } finally { 593 try { 594 if ( ps!=null ) ps.close(); 595 if ( ps2!=null ) ps2.close(); 596 System.setOut(console); // Reset standard output 597 System.setErr(consoleErr); // Reset standard err out 598 } catch (Exception e) { 599 e.printStackTrace(); 600 } 601 } 602 // TODO: verify this: 603 throw new Exception("Should not be reachable?"); 604 } 605 606 // Create ANTLR input stream based on input source, file or String 607 private CharStream getANTLRInputStream(gUnitTestInput testInput) throws IOException { 608 CharStream input; 609 if ( testInput.isFile) { 610 String filePath = testInput.input; 611 File testInputFile = new File(filePath); 612 // if input test file is not found under the current dir, try to look for it from dir where the testsuite file locates 613 if ( !testInputFile.exists() ) { 614 testInputFile = new File(this.testsuiteDir, filePath); 615 if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath(); 616 // if still not found, also try to look for it under the package dir 617 else if ( grammarInfo.getGrammarPackage()!=null ) { 618 testInputFile = new File("."+File.separator+grammarInfo.getGrammarPackage().replace(".", File.separator), filePath); 619 if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath(); 620 } 621 } 622 input = new ANTLRFileStream(filePath); 623 } 624 else { 625 input = new ANTLRStringStream(testInput.input); 626 } 627 return input; 628 } 629 630 // set up the cause of exception or the exception name into a gUnitTestResult instance 631 private gUnitTestResult getTestExceptionResult(Exception e) { 632 gUnitTestResult testResult; 633 if ( e.getCause()!=null ) { 634 testResult = new gUnitTestResult(false, e.getCause().toString(), true); 635 testResult.setError(e.getCause().toString()); 636 } 637 else { 638 testResult = new gUnitTestResult(false, e.toString(), true); 639 testResult.setError(e.toString()); 640 } 641 return testResult; 642 } 643 644 645 public void onPass(ITestCase passTest) { 646 647 } 648 649 public void onFail(ITestCase failTest) { 650 651 } 652 653} 654