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