001: /**************************************************************************/
002: /* NICE Testsuite */
003: /* A testsuite for the Nice programming language */
004: /* (c) Alex Greif 2002 */
005: /* */
006: /* This program is free software; you can redistribute it and/or modify */
007: /* it under the terms of the GNU General Public License as published by */
008: /* the Free Software Foundation; either version 2 of the License, or */
009: /* (at your option) any later version. */
010: /* */
011: /**************************************************************************/package nice.tools.testsuite;
012:
013: import java.io.*;
014: import java.util.*;
015: import java.lang.reflect.*;
016:
017: import bossa.modules.Compilation;
018:
019: /**
020: * Class represents a testcase in the testsuite file.
021: *
022: * @author Alex Greif <a href="mailto:alex.greif@web.de">alex.greif@web.de</a>
023: * @version $Id: TestCase.java,v 1.35 2005/03/24 10:00:57 bonniot Exp $
024: */
025: public abstract class TestCase {
026: /**
027: * Compiler message
028: *
029: */
030: private static final String ERROR_MSG = "Compilation failed with errors.";
031: /**
032: * Compiler message
033: *
034: */
035: private static final String BUG_MSG = "Compilation failed because of a bug in the compiler.";
036: /**
037: * Compiler message
038: *
039: */
040: private static final String WARNING_MSG = "Compilation successful despite warnings.";
041: /**
042: * Compiler message
043: *
044: */
045: private static final String OK_MSG = "Compilation successful.";
046:
047: /**
048: * TODO
049: *
050: */
051: private TestSuite _testSuite;
052: /**
053: * TODO
054: *
055: */
056: private List _niceSourceFiles = new ArrayList();
057: /**
058: * TODO
059: *
060: */
061: private NiceSourceFile _currentSourceFile;
062: /**
063: * TODO
064: *
065: */
066: private Set _dontCompilePackages = new HashSet();
067: /**
068: * TODO
069: *
070: */
071: private ByteArrayOutputStream _compilerMessagesStream;
072:
073: /**
074: * Positions where failures should occur, defined by the user.
075: *
076: */
077: private List _failPositions = new ArrayList();
078:
079: /**
080: Counter that keeps track of the lines written to the
081: testcase source file. Needed for comparisons of
082: user defined failure positions to compiler reported
083: failure positions
084: */
085: private int _lineCounter = 0;
086:
087: boolean isKnownBug;
088: boolean skip;
089: boolean noLocation;
090:
091: /**
092: * Constructor.
093: *
094: * @param suite TODO
095: */
096: public TestCase(TestSuite suite) {
097: _testSuite = suite;
098: createNewSourceFile();
099: }
100:
101: /**
102: * Creates a new SourceFile objectand adds it to the list
103: * of source files and sets the current source file to this file.
104: *
105: */
106: private void createNewSourceFile() {
107: _currentSourceFile = new NiceSourceFile();
108: _niceSourceFiles.add(_currentSourceFile);
109:
110: if (_testSuite.hasGlobalSource())
111: _currentSourceFile.addImportGlobal();
112:
113: _lineCounter = 1;
114: }
115:
116: /**
117: Returns a list of expected failure positions defined by the user.
118: */
119: protected List getFailPositions() {
120: return _failPositions;
121: }
122:
123: /**
124: * Consumes a read line from the testsuite file.
125: * Sets the status if necessary.
126: *
127: * @param line TODO
128: * @exception TestSuiteException TODO
129: */
130: public void consumeLine(String line) throws TestSuiteException {
131: //System.out.println("line " + _lineCounter + " : " + line);
132: if (consumeKeywordLine(line))
133: return;
134:
135: _lineCounter++;
136: consumeCommentedKeyword(line);
137:
138: _currentSourceFile.consumeLine(line);
139: }
140:
141: /**
142: * Checks whether the line is a keyword line and sets the status new
143: * if it is a keyword line. Packages are also recorded in the dontcompile-collection
144: * if the word "dontcompile" occures in the keyword-line. Comments are delegated to the testsuite.
145: *
146: * @param line TODO
147: * @exception TestSuiteException TODO
148: */
149: private boolean consumeKeywordLine(String line)
150: throws TestSuiteException {
151: line = line.trim();
152: if (!line.startsWith(TestNice.KEYWORD_SIGN))
153: return false;
154:
155: String keywordStatement = line.substring(
156: TestNice.KEYWORD_SIGN.length()).trim();
157: //System.out.println("keywordStatement: " + keywordStatement);
158: if (TestNice.KEYWORD_TOPLEVEL.equalsIgnoreCase(keywordStatement
159: .toLowerCase()))
160: _currentSourceFile
161: .setStatus(NiceSourceFile.STATUS_TOPLEVEL);
162: else if (keywordStatement.startsWith(TestNice.KEYWORD_PACKAGE)) {
163: if (!_currentSourceFile.isEmpty())
164: createNewSourceFile();
165:
166: _currentSourceFile.consumePackageKeyword(keywordStatement);
167: if (keywordStatement.indexOf(TestNice.KEYWORD_DONTCOMPILE) != -1)
168: _dontCompilePackages.add(_currentSourceFile
169: .getPackage());
170: } else
171: // assume its a comment and delegate it to the testsuite
172: _testSuite.consumeComment(line);
173:
174: return true;
175: }
176:
177: /**
178: Consumes keywords embedded in comments.
179: Currently only <code>FAIL HERE</code> is supported.
180: */
181: private void consumeCommentedKeyword(String line) {
182: int pos = 0;
183: while (true) {
184: int startCommentPos = line.indexOf("/*", pos);
185: if (startCommentPos == -1)
186: break;
187: int endCommentPos = line.indexOf("*/", startCommentPos + 2);
188: pos = endCommentPos;
189: //System.out.println("startCommentPos: " + startCommentPos);
190: //System.out.println("endCommentPos: " + endCommentPos);
191: if (startCommentPos == -1 || endCommentPos == -1)
192: return;
193:
194: String comment = line.substring(startCommentPos,
195: endCommentPos);
196: int keywordSignPos = line.indexOf(TestNice.KEYWORD_SIGN);
197: //System.out.println("keywordSignPos: " + keywordSignPos);
198: if (keywordSignPos < startCommentPos
199: || endCommentPos < keywordSignPos)
200: return;
201:
202: String keywordStatement = line.substring(
203: keywordSignPos + TestNice.KEYWORD_SIGN.length(),
204: endCommentPos).trim();
205: //System.out.println("keywordStatement: <" + keywordStatement + ">");
206:
207: if (TestNice.KEYWORD_FAILHERE
208: .equalsIgnoreCase(keywordStatement.toLowerCase())) {
209: // determine column
210: int columnNum = endCommentPos + 2;
211: while (Character.isWhitespace(line.charAt(columnNum)))
212: columnNum++;
213: int lineNum = _lineCounter
214: + _currentSourceFile.getCountImports() + 2;
215: //System.out.println("_lineCounter: " + _lineCounter + " _currentSourceFile.getCountImports(): " + _currentSourceFile.getCountImports());
216: _failPositions
217: .add(new FailPosition(
218: _currentSourceFile.getFileName(),
219: lineNum,
220: columnNum + 1,
221: _currentSourceFile,
222: _currentSourceFile.getStatus() == NiceSourceFile.STATUS_MAIN));
223: }
224: }
225: }
226:
227: /**
228: * Writes the files of this testcase to disk.
229: *
230: * @exception TestSuiteException TODO
231: */
232: public void writeFiles() throws TestSuiteException {
233: for (Iterator iter = _niceSourceFiles.iterator(); iter
234: .hasNext();) {
235: NiceSourceFile sourceFile = (NiceSourceFile) iter.next();
236: sourceFile.write();
237: }
238:
239: if (_testSuite.hasGlobalSource())
240: _testSuite.getGlobalSource().write();
241: }
242:
243: /**
244: * Performs the test for this testcase
245: *
246: */
247: public void performTest() {
248: TestNice.getOutput().startTestCase(this );
249: }
250:
251: /**
252: * Returns the involved packages.
253: *
254: */
255: private List getPackages() {
256: List packages = new ArrayList();
257:
258: for (Iterator iter = _niceSourceFiles.iterator(); iter
259: .hasNext();) {
260: NiceSourceFile sourceFile = (NiceSourceFile) iter.next();
261: if (!packages.contains(sourceFile.getPackage()))
262: packages.add(sourceFile.getPackage());
263: }
264:
265: return packages;
266: }
267:
268: /**
269: * Compiles all packages of this testcase except it is listed in the dontcompile-collection.
270: *
271: * @exception TestSuiteException TODO
272: * @exception CompilerBugException TODO
273: */
274: public void compilePackages() throws TestSuiteException,
275: CompilerBugException {
276: _compilerMessagesStream = new ByteArrayOutputStream();
277: PrintStream out = new PrintStream(_compilerMessagesStream);
278: PrintStream origOut = System.out;
279: PrintStream origErr = System.err;
280: System.setOut(out);
281: System.setErr(out);
282: boolean showMessages = false;
283:
284: try {
285: List packageNames = getPackages();
286: for (Iterator iter = packageNames.iterator(); iter
287: .hasNext();) {
288: String packageName = (String) iter.next();
289: if (!_dontCompilePackages.contains(packageName)) {
290: int retval = compilePackage(packageName);
291: switch (retval) {
292: case nice.tools.compiler.fun.ERROR:
293: showMessages = true;
294: throw new TestSuiteException(ERROR_MSG);
295: case nice.tools.compiler.fun.BUG:
296: showMessages = true;
297: throw new CompilerBugException(BUG_MSG);
298: case nice.tools.compiler.fun.WARNING:
299: showMessages = true;
300: break;
301: }
302: }
303: }
304: } finally {
305: System.setOut(origOut);
306: System.setErr(origErr);
307: try {
308: _compilerMessagesStream.close();
309: out.close();
310: } catch (IOException e) {
311: }
312: }
313: }
314:
315: /**
316: * Compiles the specified package of this testcase.
317: *
318: * @param packageName TODO
319: * @exception TestSuiteException TODO
320: */
321: private int compilePackage(String packageName)
322: throws TestSuiteException {
323: nice.tools.compiler.console.ConsoleOutput output = nice.tools.compiler.console.fun
324: .consoleOutput();
325: Compilation compilation = bossa.modules.fun.createCompilation(
326: output, new bossa.parser.JavaccParser());
327: String tempDir = TestNice.getTempFolder().getAbsolutePath();
328: compilation.sourcePath = tempDir;
329: compilation.destinationDir = tempDir;
330: compilation.runtimeFile = TestNice.getRuntime();
331:
332: if (TestNice.getGcc() == null)
333: nice.tools.compiler.fun.compile(compilation, packageName,
334: null, null, false);
335: else {
336: compilation.output = new File(tempDir, "testcase.jar")
337: .getPath();
338: nice.tools.compiler.fun.compile(compilation, packageName,
339: new File(tempDir, "testcase.exe").getPath(),
340: TestNice.getGcc() + "/bin/gcj", false);
341: }
342:
343: return output.statusCode;
344: }
345:
346: public void runNative() throws TestSuiteException {
347: try {
348: String[] env = null;
349: if (TestNice.getGcc() != null)
350: env = new String[] { "LD_LIBRARY_PATH="
351: + TestNice.getGcc() + "/lib" };
352:
353: Process p = Runtime.getRuntime().exec(
354: TestNice.getTempFolder() + "/testcase.exe", env);
355: CharArrayWriter out = new CharArrayWriter();
356: int exitValue = nice.tools.compiler.dispatch
357: .waitFor(p, out);
358: // Print the output of the execution.
359: System.out.println(out.toString());
360: if (exitValue != 0)
361: throw new TestSuiteException("Exit code: " + exitValue);
362: } catch (IOException e) {
363: throw new TestSuiteException(e.getMessage());
364: }
365: }
366:
367: public void runJVM(String jvm, String main)
368: throws TestSuiteException {
369: try {
370: Process p = Runtime.getRuntime().exec(
371: jvm + " -classpath " + TestNice.getTempFolder()
372: + ":classes " + main);
373: CharArrayWriter out = new CharArrayWriter();
374: int exitValue = nice.tools.compiler.dispatch
375: .waitFor(p, out);
376: // Print the output of the execution.
377: System.out.println(out.toString());
378: if (exitValue != 0)
379: throw new TestSuiteException("Exit code: " + exitValue);
380: } catch (IOException e) {
381: throw new TestSuiteException(e.getMessage());
382: }
383: }
384:
385: /**
386: * Runs the main method of the testcase. Only if main method exists and
387: * the package was compiled.
388: *
389: * @exception TestSuiteException TODO
390: */
391: public void runMain() throws TestSuiteException {
392: ByteArrayOutputStream mainMessagesStream = new ByteArrayOutputStream();
393: PrintStream out = new PrintStream(mainMessagesStream);
394: PrintStream origOut = System.out;
395: PrintStream origErr = System.err;
396: System.setOut(out);
397: System.setErr(out);
398: try {
399:
400: if (TestNice.getGcc() != null) {
401: runNative();
402: return;
403: }
404:
405: for (Iterator iter = _niceSourceFiles.iterator(); iter
406: .hasNext();) {
407: NiceSourceFile sourceFile = (NiceSourceFile) iter
408: .next();
409: if (sourceFile.hasMainMethod()
410: && !_dontCompilePackages.contains(sourceFile
411: .getPackage())) {
412: if (TestNice.getJVM() != null) {
413: runJVM(TestNice.getJVM(), sourceFile
414: .getPackage()
415: + ".fun");
416: continue;
417: }
418:
419: ClassLoader loader = TestNice.getClassLoader();
420: try {
421: Class c = Class.forName(sourceFile.getPackage()
422: + ".fun", true, loader);
423: Class[] parameterTypes = new Class[] { String[].class };
424: Method m = c.getMethod("main", parameterTypes);
425: Object[] arguments = new Object[] { new String[0] };
426: try {
427: m.invoke(c.newInstance(), arguments);
428: } catch (java.lang.reflect.InvocationTargetException e) {
429: throw e.getTargetException();
430: }
431: } catch (Throwable e) {
432: nice.lang.dispatch
433: .printStackTraceWithSourceInfo(e, out,
434: loader);
435: throw new TestSuiteException(
436: "Exception while invoking main()", e);
437: }
438: }
439: }
440: } finally {
441: System.setOut(origOut);
442: System.setErr(origErr);
443: try {
444: mainMessagesStream.close();
445: out.close();
446: } catch (IOException e) {
447: }
448: if (mainMessagesStream.size() != 0)
449: TestNice.getOutput().log("main",
450: mainMessagesStream.toString());
451: }
452: }
453:
454: /**
455: * Called by a specific testcase if the testcase succeeds.
456: * This is the place for output messages.
457: *
458: */
459: public void pass() {
460: if (isKnownBug) {
461: TestNice.increaseFixed();
462: printSources();
463: TestNice.getOutput().logAndFlush(
464: "The above known bug is now FIXED!");
465: } else
466: TestNice.increaseSucceeded();
467: TestNice.getOutput().endTestCase(true);
468: }
469:
470: /**
471: * Called by a specific testcase if the testcase fails.
472: * This is the place for output messages.
473: */
474: public void fail() {
475: if (isKnownBug) {
476: TestNice.increaseKnownBug();
477: TestNice.getOutput().endTestCase(true);
478: } else {
479: TestNice.increaseFailed();
480:
481: // log the sources
482: printSources();
483:
484: // compiler messages
485: TestNice.getOutput().log("nicec", getCompilerMessages());
486: TestNice.getOutput().endTestCase(false);
487: }
488:
489: // move contents of temp folder to a new folder in the fail folder
490: TestNice.moveFilesToFailFolder();
491: }
492:
493: /**
494: Called if a warning occures. For example if FAIL_HERE is at a wrong position.
495: */
496: public void warning() {
497: TestNice.increaseWarning();
498:
499: // log the sources
500: printSources();
501:
502: // compiler messages
503: TestNice.getOutput().log("nicec", getCompilerMessages());
504: TestNice.getOutput().endTestCase(false);
505:
506: // move contents of temp folder to a new folder in the fail folder
507: TestNice.moveFilesToFailFolder();
508: }
509:
510: /**
511: Print global and local sources that are involved in this testcase.
512: */
513: private void printSources() {
514: BufferedWriter writer;
515: StringWriter contentWriter;
516: // ordinary files
517: for (Iterator iter = _niceSourceFiles.iterator(); iter
518: .hasNext();) {
519: NiceSourceFile sourceFile = (NiceSourceFile) iter.next();
520: contentWriter = new StringWriter();
521: writer = new BufferedWriter(contentWriter);
522: try {
523: sourceFile.write(writer);
524: contentWriter.close();
525: writer.close();
526:
527: TestNice.getOutput().log("");
528:
529: LineNumberReader lines = new LineNumberReader(
530: new StringReader(contentWriter.toString()));
531: String file = "file " + sourceFile.getPackage() + "."
532: + sourceFile.getFileName();
533: String line;
534: while ((line = lines.readLine()) != null)
535: TestNice.getOutput().log(
536: file + ":" + lines.getLineNumber(), line);
537: } catch (IOException e) {
538: e.printStackTrace();
539: }
540:
541: TestNice.getOutput().log("");
542: }
543: // global file
544: if (_testSuite.hasGlobalSource()) {
545: contentWriter = new StringWriter();
546: writer = new BufferedWriter(contentWriter);
547: try {
548: _testSuite.getGlobalSource().write(writer);
549: contentWriter.close();
550: writer.close();
551: } catch (IOException e) {
552: e.printStackTrace();
553: }
554: TestNice.getOutput().log(
555: "file "
556: + _testSuite.getGlobalSource().getPackage()
557: + "."
558: + _testSuite.getGlobalSource()
559: .getFileName(),
560: contentWriter.toString());
561: TestNice.getOutput().log("");
562: }
563: }
564:
565: /**
566: Returns the compiler messages as String
567: */
568: protected String getCompilerMessages() {
569: return _compilerMessagesStream.toString();
570: }
571:
572: /**
573: InnerClass that holds a user defined failure position.
574: */
575: protected class FailPosition {
576: private String _fileName;
577: private int _line;
578: private int _column;
579: private NiceSourceFile _sourceFile;
580: private boolean _inMain;
581:
582: FailPosition(String fileName, int line, int column,
583: NiceSourceFile sourceFile, boolean inMain) {
584: _fileName = fileName;
585: _line = line;
586: _column = column;
587: _sourceFile = sourceFile;
588: _inMain = inMain;
589: }
590:
591: protected String getFileName() {
592: return _fileName;
593: }
594:
595: protected int getLine() {
596: int res = _line;
597: if (_inMain)
598: // Add the number of lines of the toplevel code,
599: // plus two lines for the main section header.
600: res += _sourceFile.getTopLevelSectionLength() + 2;
601: else
602: // The main method is moved after toplevel, so adjust by removing
603: // that count.
604: res -= _sourceFile.getMainSectionLength();
605: return res;
606: }
607:
608: protected int getColumn() {
609: return _column;
610: }
611: }
612:
613: }
614:
615: // Local Variables:
616: // tab-width: 2
617: // indent-tabs-mode: t
618: // End:
|