| /* |
| [The "BSD licence"] |
| Copyright (c) 2007-2008 Leon, Jen-Yuan Su |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| 3. The name of the author may not be used to endorse or promote products |
| derived from this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package org.antlr.gunit; |
| |
| import junit.framework.TestCase; |
| import org.antlr.runtime.*; |
| import org.antlr.runtime.tree.CommonTree; |
| import org.antlr.runtime.tree.CommonTreeNodeStream; |
| import org.antlr.runtime.tree.TreeAdaptor; |
| import org.antlr.runtime.tree.TreeNodeStream; |
| import org.antlr.stringtemplate.StringTemplate; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.PrintStream; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| |
| /** All gUnit-generated JUnit class should extend this class |
| * which implements the essential methods for triggering |
| * ANTLR parser/tree walker |
| */ |
| public abstract class gUnitBaseTest extends TestCase { |
| |
| public String treeAdaptorPath; |
| public String packagePath; |
| public String lexerPath; |
| public String parserPath; |
| public String treeParserPath; |
| |
| protected String stdout; |
| protected String stderr; |
| |
| private PrintStream console = System.out; |
| private PrintStream consoleErr = System.err; |
| |
| // Invoke target lexer.rule |
| public String execLexer(String testRuleName, int line, String testInput, boolean isFile) throws Exception { |
| CharStream input; |
| /** Set up ANTLR input stream based on input source, file or String */ |
| if ( isFile ) { |
| String filePath = testInput; |
| File testInputFile = new File(filePath); |
| // if input test file is not found under the current dir, also try to look for it under the package dir |
| if ( !testInputFile.exists() && packagePath!=null ) { |
| testInputFile = new File(packagePath, filePath); |
| if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath(); |
| } |
| input = new ANTLRFileStream(filePath); |
| } |
| else { |
| input = new ANTLRStringStream(testInput); |
| } |
| Class lexer = null; |
| PrintStream ps = null; // for redirecting stdout later |
| PrintStream ps2 = null; // for redirecting stderr later |
| try { |
| /** Use Reflection to create instances of lexer and parser */ |
| lexer = Class.forName(lexerPath); |
| Class[] lexArgTypes = new Class[]{CharStream.class}; // assign type to lexer's args |
| Constructor lexConstructor = lexer.getConstructor(lexArgTypes); |
| Object[] lexArgs = new Object[]{input}; // assign value to lexer's args |
| Lexer lexObj = (Lexer)lexConstructor.newInstance(lexArgs); // makes new instance of lexer |
| input.setLine(line); |
| |
| Method ruleName = lexer.getMethod("m"+testRuleName, new Class[0]); |
| |
| /** Start of I/O Redirecting */ |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| ByteArrayOutputStream err = new ByteArrayOutputStream(); |
| ps = new PrintStream(out); |
| ps2 = new PrintStream(err); |
| System.setOut(ps); |
| System.setErr(ps2); |
| /** End of redirecting */ |
| |
| /** Invoke lexer rule, and get the current index in CharStream */ |
| ruleName.invoke(lexObj, new Object[0]); |
| Method ruleName2 = lexer.getMethod("getCharIndex", new Class[0]); |
| int currentIndex = (Integer) ruleName2.invoke(lexObj, new Object[0]); |
| if ( currentIndex!=input.size() ) { |
| ps2.println("extra text found, '"+input.substring(currentIndex, input.size()-1)+"'"); |
| } |
| |
| this.stdout = null; |
| this.stderr = null; |
| |
| if ( err.toString().length()>0 ) { |
| this.stderr = err.toString(); |
| return this.stderr; |
| } |
| if ( out.toString().length()>0 ) { |
| this.stdout = out.toString(); |
| } |
| if ( err.toString().length()==0 && out.toString().length()==0 ) { |
| return null; |
| } |
| } catch (ClassNotFoundException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (SecurityException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (NoSuchMethodException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (IllegalArgumentException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (InstantiationException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (IllegalAccessException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (InvocationTargetException e) { // This exception could be caused from ANTLR Runtime Exception, e.g. MismatchedTokenException |
| if ( e.getCause()!=null ) this.stderr = e.getCause().toString(); |
| else this.stderr = e.toString(); |
| return this.stderr; |
| } finally { |
| try { |
| if ( ps!=null ) ps.close(); |
| if ( ps2!=null ) ps2.close(); |
| System.setOut(console); // Reset standard output |
| System.setErr(consoleErr); // Reset standard err out |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| return this.stdout; |
| } |
| |
| // Invoke target parser.rule |
| |
| public Object execParser(String testRuleName, int line, String testInput, boolean isFile) throws Exception { |
| CharStream input; |
| /** Set up ANTLR input stream based on input source, file or String */ |
| if ( isFile ) { |
| String filePath = testInput; |
| File testInputFile = new File(filePath); |
| // if input test file is not found under the current dir, also try to look for it under the package dir |
| if ( !testInputFile.exists() && packagePath!=null ) { |
| testInputFile = new File(packagePath, filePath); |
| if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath(); |
| } |
| input = new ANTLRFileStream(filePath); |
| } |
| else { |
| input = new ANTLRStringStream(testInput); |
| } |
| Class lexer = null; |
| Class parser = null; |
| PrintStream ps = null; // for redirecting stdout later |
| PrintStream ps2 = null; // for redirecting stderr later |
| ByteArrayOutputStream out = null; |
| ByteArrayOutputStream err = null; |
| try { |
| /** Use Reflection to create instances of lexer and parser */ |
| lexer = Class.forName(lexerPath); |
| Class[] lexArgTypes = new Class[]{CharStream.class}; // assign type to lexer's args |
| Constructor lexConstructor = lexer.getConstructor(lexArgTypes); |
| Object[] lexArgs = new Object[]{input}; // assign value to lexer's args |
| Lexer lexObj = (Lexer)lexConstructor.newInstance(lexArgs); // makes new instance of lexer |
| input.setLine(line); |
| |
| CommonTokenStream tokens = new CommonTokenStream(lexObj); |
| parser = Class.forName(parserPath); |
| Class[] parArgTypes = new Class[]{TokenStream.class}; // assign type to parser's args |
| Constructor parConstructor = parser.getConstructor(parArgTypes); |
| Object[] parArgs = new Object[]{tokens}; // assign value to parser's args |
| Parser parObj = (Parser)parConstructor.newInstance(parArgs); // makes new instance of parser |
| |
| // set up customized tree adaptor if necessary |
| if ( treeAdaptorPath!=null ) { |
| parArgTypes = new Class[]{TreeAdaptor.class}; |
| Method _setTreeAdaptor = parser.getMethod("setTreeAdaptor", parArgTypes); |
| Class _treeAdaptor = Class.forName(treeAdaptorPath); |
| _setTreeAdaptor.invoke(parObj, _treeAdaptor.newInstance()); |
| } |
| |
| Method ruleName = parser.getMethod(testRuleName); |
| |
| /** Start of I/O Redirecting */ |
| out = new ByteArrayOutputStream(); |
| err = new ByteArrayOutputStream(); |
| ps = new PrintStream(out); |
| ps2 = new PrintStream(err); |
| System.setOut(ps); |
| System.setErr(ps2); |
| /** End of redirecting */ |
| |
| /** Invoke grammar rule, and store if there is a return value */ |
| Object ruleReturn = ruleName.invoke(parObj); |
| String astString = null; |
| String stString = null; |
| /** If rule has return value, determine if it contains an AST or a ST */ |
| if ( ruleReturn!=null ) { |
| if ( ruleReturn.getClass().toString().indexOf(testRuleName+"_return")>0 ) { |
| try { // NullPointerException may happen here... |
| Class _return = Class.forName(parserPath+"$"+testRuleName+"_return"); |
| Method[] methods = _return.getDeclaredMethods(); |
| for(Method method : methods) { |
| if ( method.getName().equals("getTree") ) { |
| Method returnName = _return.getMethod("getTree"); |
| CommonTree tree = (CommonTree) returnName.invoke(ruleReturn); |
| astString = tree.toStringTree(); |
| } |
| else if ( method.getName().equals("getTemplate") ) { |
| Method returnName = _return.getMethod("getTemplate"); |
| StringTemplate st = (StringTemplate) returnName.invoke(ruleReturn); |
| stString = st.toString(); |
| } |
| } |
| } |
| catch(Exception e) { |
| System.err.println(e); // Note: If any exception occurs, the test is viewed as failed. |
| } |
| } |
| } |
| |
| this.stdout = ""; |
| this.stderr = ""; |
| |
| /** Invalid input */ |
| if ( tokens.index()!=tokens.size()-1 ) { |
| //throw new InvalidInputException(); |
| this.stderr += "Stopped parsing at token index "+tokens.index()+": "; |
| } |
| |
| // retVal could be actual return object from rule, stderr or stdout |
| this.stdout += out.toString(); |
| this.stderr += err.toString(); |
| |
| if ( err.toString().length()>0 ) return this.stderr; |
| if ( out.toString().length()>0 ) return this.stdout; |
| if ( astString!=null ) { // Return toStringTree of AST |
| return astString; |
| } |
| else if ( stString!=null ) {// Return toString of ST |
| return stString; |
| } |
| if ( ruleReturn!=null ) { |
| return ruleReturn; |
| } |
| if ( err.toString().length()==0 && out.toString().length()==0 ) { |
| return null; |
| } |
| } |
| catch (ClassNotFoundException e) { |
| e.printStackTrace(); System.exit(1); |
| } |
| catch (SecurityException e) { |
| e.printStackTrace(); System.exit(1); |
| } |
| catch (NoSuchMethodException e) { |
| e.printStackTrace(); System.exit(1); |
| } |
| catch (IllegalAccessException e) { |
| e.printStackTrace(); System.exit(1); |
| } |
| catch (InvocationTargetException e) { |
| this.stdout = out.toString(); |
| this.stderr = err.toString(); |
| |
| if ( e.getCause()!=null ) this.stderr += e.getCause().toString(); |
| else this.stderr += e.toString(); |
| return this.stderr; |
| } finally { |
| try { |
| if ( ps!=null ) ps.close(); |
| if ( ps2!=null ) ps2.close(); |
| System.setOut(console); // Reset standard output |
| System.setErr(consoleErr); // Reset standard err out |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| return this.stdout; |
| } |
| |
| // Invoke target parser.rule |
| public Object execTreeParser(String testTreeRuleName, String testRuleName, String testInput, boolean isFile) throws Exception { |
| CharStream input; |
| if ( isFile ) { |
| String filePath = testInput; |
| File testInputFile = new File(filePath); |
| // if input test file is not found under the current dir, also try to look for it under the package dir |
| if ( !testInputFile.exists() && packagePath!=null ) { |
| testInputFile = new File(packagePath, filePath); |
| if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath(); |
| } |
| input = new ANTLRFileStream(filePath); |
| } |
| else { |
| input = new ANTLRStringStream(testInput); |
| } |
| Class lexer = null; |
| Class parser = null; |
| Class treeParser = null; |
| PrintStream ps = null; // for redirecting stdout later |
| PrintStream ps2 = null; // for redirecting stderr later |
| try { |
| /** Use Reflection to create instances of lexer and parser */ |
| lexer = Class.forName(lexerPath); |
| Class[] lexArgTypes = new Class[]{CharStream.class}; // assign type to lexer's args |
| Constructor lexConstructor = lexer.getConstructor(lexArgTypes); |
| Object[] lexArgs = new Object[]{input}; // assign value to lexer's args |
| Object lexObj = lexConstructor.newInstance(lexArgs); // makes new instance of lexer |
| |
| CommonTokenStream tokens = new CommonTokenStream((Lexer) lexObj); |
| |
| parser = Class.forName(parserPath); |
| Class[] parArgTypes = new Class[]{TokenStream.class}; // assign type to parser's args |
| Constructor parConstructor = parser.getConstructor(parArgTypes); |
| Object[] parArgs = new Object[]{tokens}; // assign value to parser's args |
| Object parObj = parConstructor.newInstance(parArgs); // makes new instance of parser |
| |
| // set up customized tree adaptor if necessary |
| TreeAdaptor customTreeAdaptor = null; |
| if ( treeAdaptorPath!=null ) { |
| parArgTypes = new Class[]{TreeAdaptor.class}; |
| Method _setTreeAdaptor = parser.getMethod("setTreeAdaptor", parArgTypes); |
| Class _treeAdaptor = Class.forName(treeAdaptorPath); |
| customTreeAdaptor = (TreeAdaptor) _treeAdaptor.newInstance(); |
| _setTreeAdaptor.invoke(parObj, customTreeAdaptor); |
| } |
| |
| Method ruleName = parser.getMethod(testRuleName); |
| |
| /** Start of I/O Redirecting */ |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| ByteArrayOutputStream err = new ByteArrayOutputStream(); |
| ps = new PrintStream(out); |
| ps2 = new PrintStream(err); |
| System.setOut(ps); |
| System.setErr(ps2); |
| /** End of redirecting */ |
| |
| /** Invoke grammar rule, and get the return value */ |
| Object ruleReturn = ruleName.invoke(parObj); |
| |
| Class _return = Class.forName(parserPath+"$"+testRuleName+"_return"); |
| Method returnName = _return.getMethod("getTree"); |
| CommonTree tree = (CommonTree) returnName.invoke(ruleReturn); |
| |
| // Walk resulting tree; create tree nodes stream first |
| CommonTreeNodeStream nodes; |
| if ( customTreeAdaptor!=null ) { |
| nodes = new CommonTreeNodeStream(customTreeAdaptor, tree); |
| } |
| else { |
| nodes = new CommonTreeNodeStream(tree); |
| } |
| // AST nodes have payload that point into token stream |
| nodes.setTokenStream(tokens); |
| // Create a tree walker attached to the nodes stream |
| treeParser = Class.forName(treeParserPath); |
| Class[] treeParArgTypes = new Class[]{TreeNodeStream.class}; // assign type to tree parser's args |
| Constructor treeParConstructor = treeParser.getConstructor(treeParArgTypes); |
| Object[] treeParArgs = new Object[]{nodes}; // assign value to tree parser's args |
| Object treeParObj = treeParConstructor.newInstance(treeParArgs); // makes new instance of tree parser |
| // Invoke the tree rule, and store the return value if there is |
| Method treeRuleName = treeParser.getMethod(testTreeRuleName); |
| Object treeRuleReturn = treeRuleName.invoke(treeParObj); |
| |
| String astString = null; |
| String stString = null; |
| /** If tree rule has return value, determine if it contains an AST or a ST */ |
| if ( treeRuleReturn!=null ) { |
| if ( treeRuleReturn.getClass().toString().indexOf(testTreeRuleName+"_return")>0 ) { |
| try { // NullPointerException may happen here... |
| Class _treeReturn = Class.forName(treeParserPath+"$"+testTreeRuleName+"_return"); |
| Method[] methods = _treeReturn.getDeclaredMethods(); |
| for(Method method : methods) { |
| if ( method.getName().equals("getTree") ) { |
| Method treeReturnName = _treeReturn.getMethod("getTree"); |
| CommonTree returnTree = (CommonTree) treeReturnName.invoke(treeRuleReturn); |
| astString = returnTree.toStringTree(); |
| } |
| else if ( method.getName().equals("getTemplate") ) { |
| Method treeReturnName = _return.getMethod("getTemplate"); |
| StringTemplate st = (StringTemplate) treeReturnName.invoke(treeRuleReturn); |
| stString = st.toString(); |
| } |
| } |
| } |
| catch(Exception e) { |
| System.err.println(e); // Note: If any exception occurs, the test is viewed as failed. |
| } |
| } |
| } |
| |
| this.stdout = null; |
| this.stderr = null; |
| |
| /** Invalid input */ |
| if ( tokens.index()!=tokens.size()-1 ) { |
| throw new InvalidInputException(); |
| } |
| |
| // retVal could be actual return object from rule, stderr or stdout |
| if ( err.toString().length()>0 ) { |
| this.stderr = err.toString(); |
| return this.stderr; |
| } |
| if ( out.toString().length()>0 ) { |
| this.stdout = out.toString(); |
| } |
| if ( astString!=null ) { // Return toStringTree of AST |
| return astString; |
| } |
| else if ( stString!=null ) {// Return toString of ST |
| return stString; |
| } |
| if ( treeRuleReturn!=null ) { |
| return treeRuleReturn; |
| } |
| if ( err.toString().length()==0 && out.toString().length()==0 ) { |
| return null; |
| } |
| } catch (ClassNotFoundException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (SecurityException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (NoSuchMethodException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (IllegalAccessException e) { |
| e.printStackTrace(); System.exit(1); |
| } catch (InvocationTargetException e) { |
| if ( e.getCause()!=null ) this.stderr = e.getCause().toString(); |
| else this.stderr = e.toString(); |
| return this.stderr; |
| } finally { |
| try { |
| if ( ps!=null ) ps.close(); |
| if ( ps2!=null ) ps2.close(); |
| System.setOut(console); // Reset standard output |
| System.setErr(consoleErr); // Reset standard err out |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| return stdout; |
| } |
| |
| // Modify the return value if the expected token type is OK or FAIL |
| public Object examineExecResult(int tokenType, Object retVal) { |
| if ( tokenType==gUnitParser.OK ) { // expected Token: OK |
| if ( this.stderr==null ) { |
| return "OK"; |
| } |
| else { |
| return "FAIL, "+this.stderr; |
| } |
| } |
| else if ( tokenType==gUnitParser.FAIL ) { // expected Token: FAIL |
| if ( this.stderr!=null ) { |
| return "FAIL"; |
| } |
| else { |
| return "OK"; |
| } |
| } |
| else { // return the same object for the other token types |
| return retVal; |
| } |
| } |
| |
| } |