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.net.URL;
016:
017: import nice.tools.testsuite.output.*;
018:
019: /**
020: * Example:
021: * /// pass
022: * /// package a dontcompile
023: * int x = 3;
024: * System.out.println("hallo world");
025: * /// toplevel
026: * void f() {
027: * System.out.println("f()");
028: * }
029: * /// package b import a
030: * int z = 5;
031: * f();
032: *
033: * @author Alex Greif <a href="mailto:alex.greif@web.de">alex.greif@web.de</a>
034: * @version $Id: TestNice.java,v 1.35 2005/06/17 11:09:51 bonniot Exp $
035: */
036:
037: public class TestNice {
038:
039: /**
040: * Testsuite file extension.
041: *
042: */
043: static private final String TESTSUITE_FILE_EXTENSION = ".testsuite";
044:
045: /**
046: * Keyword indicator.
047: *
048: */
049: static final String KEYWORD_SIGN = "///";
050:
051: /**
052: * Keyword.
053: *
054: */
055: static final String KEYWORD_TOPLEVEL = "toplevel";
056:
057: /**
058: * Keyword.
059: *
060: */
061: static final String KEYWORD_PACKAGE = "package";
062:
063: /**
064: * Keyword.
065: *
066: */
067: static final String KEYWORD_IMPORT = "import";
068:
069: /**
070: * Keyword.
071: *
072: */
073: static final String KEYWORD_DONTCOMPILE = "dontcompile";
074:
075: /**
076: * Keyword.
077: *
078: */
079: static final String KEYWORD_FAILHERE = "fail here";
080:
081: /**
082: * The temporary folder where nice sources and the compiled bytecod is placed.
083: *
084: */
085: private static File _tempFolder = new File("testsuite-temp-folder");
086:
087: /**
088: * The temporary folder where failed testcases are placed.
089: *
090: */
091: private static File _failFolder = new File("testsuite-fail-folder");
092:
093: /**
094: * TODO
095: *
096: */
097: private static int _fileCounter = 0;
098:
099: /**
100: * Number of successful testcases.
101: *
102: */
103: private static int _testCasesSucceeded = 0;
104:
105: /**
106: * Number of failed testcases.
107: *
108: */
109: private static int _testCasesFailed = 0;
110:
111: /**
112: * Number of testcases with warnings. These are ahndled as failed testcases.
113: *
114: */
115: private static int _testCasesWarning = 0;
116:
117: private static int _testCasesKnownBug = 0;
118:
119: private static int _testCasesFixed = 0;
120:
121: /**
122: * The Output where log statements should be written.
123: * ConsoleOutput is the default Output.
124: *
125: */
126: private static Output _output = new ConsoleOutput();
127:
128: /**
129: The <b>home</b> directory of gcc.
130: If you want to use the system-wide version of gcc on a Unix machine,
131: this should likely be set to "/usr".
132: */
133: private static String _gcc = null;
134:
135: /**
136: A JVM with which to run generated code (in a separate process).
137: Useful for testing alternative JVMs like Kaffe without using them for
138: running the testengine and the compilation itself.
139: */
140: private static String _jvm = null;
141:
142: /**
143: * TODO
144: *
145: */
146: private static List _testSuites = new ArrayList();
147:
148: /**
149: * Flag whether comments should be included in the output or not.
150: *
151: */
152: private static boolean _writeComments;
153:
154: /**
155: Whether to wait at the end of the run.
156: This is useful for profiling memory after all the work is done,
157: using a debugger/memory profiler.
158: */
159: private static boolean _wait;
160:
161: /**
162: * Classpath entry that contains the Nice standard library.
163: *
164: * This is only needed when the testsuite is not run from a JVM
165: * (for instance after native compilation), or if the standard library
166: * is not in the classpath.
167: *
168: */
169: private static String _runtime;
170:
171: /**
172: * Main method of the application.
173: *
174: * Returns to the system with the code:
175: * 0 if there are no regressions;
176: * 1 if there was at least one regression;
177: * 2 if there was at least one warning on no regression;
178: *
179: * @param args console arguments
180: */
181: static public void main(String[] args) {
182:
183: _runtime = nice.tools.compiler.dispatch.getNiceRuntime();
184:
185: if (!processArgs(args)) {
186: usage();
187: System.exit(1);
188: }
189:
190: /*try {
191: _output = new HtmlOutput(new FileWriter(new File(TestNice.getTempFolder().getParent(), "testsuite_output.html")));
192: } catch(IOException e) {
193: e.printStackTrace();
194: }
195: _output = new ConsoleOutput();
196: */
197:
198: _output.startApplication();
199: cleanupTempFolder();
200: cleanupFailFolder();
201:
202: try {
203: // iterate through testsuites
204: for (Iterator iter = _testSuites.iterator(); iter.hasNext();)
205: new TestNice().performTests((String) iter.next());
206: } catch (TestSuiteException e) {
207: e.printStackTrace();
208: }
209:
210: if (_wait) {
211: reclaimMemory(true);
212:
213: System.out
214: .println("Test finished.\nPress return to terminate:");
215: try {
216: new DataInputStream(System.in).readLine();
217: } catch (IOException e) {
218: }
219: }
220:
221: _output.endApplication();
222:
223: // close writer
224: _output.close();
225:
226: if (getTestCasesFailed() > 0)
227: System.exit(1);
228:
229: if (getTestCasesWarning() > 0)
230: System.exit(2);
231: }
232:
233: private static void reclaimMemory(boolean clear) {
234: // Reclaim memory.
235: bossa.modules.Package.startNewCompilation();
236: mlsub.typing.NullnessKind.setSure(null);
237: mlsub.typing.NullnessKind.setMaybe(null);
238:
239: // Create an OutOfMemoryError, so that soft references are also let loose.
240: if (clear)
241: try {
242: int[] l = new int[1000];
243: for (;;) {
244: l = new int[l.length * 2];
245: }
246: } catch (OutOfMemoryError e) {
247: }
248:
249: System.gc();
250:
251: nice.tools.compiler.console.fun.printMemoryUsage();
252: }
253:
254: /**
255: * Processes the command line arguments. Returns true if everything is ok.
256: *
257: * @param args TODO
258: */
259: private static boolean processArgs(String[] args) {
260: for (int i = 0; i < args.length;) {
261: String s = args[i];
262: i++;
263: if (s.startsWith("-")) {
264: if ("-output".equalsIgnoreCase(s))
265: setOutput(args[i++]);
266: else if ("-gcc".equalsIgnoreCase(s))
267: setGcc(args[i++]);
268: else if ("-jvm".equalsIgnoreCase(s))
269: setJvm(args[i++]);
270: else if ("-comment".equalsIgnoreCase(s))
271: _writeComments = true;
272: else if ("-runtime".equalsIgnoreCase(s))
273: _runtime = args[i++];
274: else if ("-wait".equalsIgnoreCase(s))
275: _wait = true;
276: else
277: return false;
278: } else
279: _testSuites.add(s);
280: }
281: return true;
282: }
283:
284: /**
285: * Sets the output of the test engine
286: *
287: * @param output TODO
288: */
289: private static void setOutput(String output) {
290: if ("console".equalsIgnoreCase(output)) {
291: _output = new ConsoleOutput();
292: return;
293: }
294:
295: output = output.toLowerCase();
296: if (output.endsWith(".html") || output.endsWith(".htm"))
297: try {
298: _output = new HtmlOutput(new FileWriter(
299: new File(output)));
300: } catch (IOException e) {
301: e.printStackTrace();
302: }
303: }
304:
305: private static void setGcc(String gcc) {
306: _gcc = gcc;
307:
308: // When doing native compilation, we need a jarred runtime
309: if (_runtime == null || !_runtime.endsWith(".jar"))
310: _runtime = "share/java/nice.jar";
311: }
312:
313: private static void setJvm(String jvm) {
314: _jvm = jvm;
315: }
316:
317: /**
318: * Prints the usage of this application.
319: *
320: */
321: static private void usage() {
322: System.out
323: .println("usage:\n java nice.tools.testsuiteTestNice [testSuiteFile | folder]");
324: }
325:
326: /**
327: * Deletes the temporary folder with all its contents and
328: * creates a new empty one.
329: *
330: */
331: static void cleanupTempFolder() {
332: if (_tempFolder.exists())
333: deleteFolder(_tempFolder);
334:
335: _tempFolder.mkdir();
336: }
337:
338: /**
339: * Deletes the fail folder with all its contents and
340: * creates a new empty one.
341: *
342: */
343: static private void cleanupFailFolder() {
344: if (_failFolder.exists())
345: deleteFolder(_failFolder);
346:
347: _failFolder.mkdir();
348: }
349:
350: /**
351: * Moves all files and folders of the temp folder to a
352: * newly created folder inside the fail folder
353: *
354: */
355: static void moveFilesToFailFolder() {
356: File folder = new File(_failFolder, ""
357: + (_testCasesFailed + _testCasesKnownBug));
358: _tempFolder.renameTo(folder);
359:
360: _tempFolder.mkdir();
361: }
362:
363: /**
364: * TODO
365: *
366: */
367: static int getFileCounter() {
368: return ++_fileCounter;
369: }
370:
371: /**
372: * deletes the specified folder and all its contents.
373: *
374: * @param folder The folder that should be deleted
375: */
376: private static void deleteFolder(File folder) {
377: File[] files = folder.listFiles();
378: for (int i = 0; i < files.length; i++) {
379: File file = files[i];
380: if (file.isDirectory())
381: deleteFolder(file);
382: else
383: file.delete();
384: }
385: folder.delete();
386: }
387:
388: /**
389: * Performs the tests in a single testsuite file or in all testsuite files in
390: * a folder
391: *
392: * @param testSuitePath TODO
393: * @exception TestSuiteException TODO
394: * @exception TestSuiteException TODO
395: * @exception TestSuiteException TODO
396: */
397: private void performTests(String testSuitePath)
398: throws TestSuiteException {
399: File file = new File(testSuitePath);
400: if (!file.exists())
401: throw new TestSuiteException(
402: "Could not find testsuite file or folder: "
403: + file.getAbsolutePath());
404: //TestSuite testsuite = new TestSuite(new File("/Users/agreif/projects/Nice/sf/testsuite/compiler/first.testsuite"));
405: if (file.isFile()) {
406: new TestSuite(file);
407: return;
408: }
409:
410: // collect all testsuite files and perform its tests
411: Set testSuiteFiles = new HashSet();
412: getTestSuiteFiles(file, testSuiteFiles);
413:
414: // sort the files on last modification time.
415: List files = new ArrayList(testSuiteFiles);
416: Collections.sort(files, fileComp);
417:
418: for (Iterator iter = files.iterator(); iter.hasNext();)
419: new TestSuite((File) iter.next());
420: }
421:
422: private static Comparator fileComp = new FileComparator();
423:
424: private static class FileComparator implements Comparator {
425: public FileComparator() {
426: }
427:
428: public int compare(Object o1, Object o2) {
429: File file1 = (File) o1;
430: File file2 = (File) o2;
431: if (file1.lastModified() < file2.lastModified())
432: return 1;
433:
434: if (file1.lastModified() > file2.lastModified())
435: return -1;
436:
437: return 0;
438: }
439:
440: public boolean equals(Object obj) {
441: return false;
442: }
443: }
444:
445: /**
446: * Collects all testsuite files in the specified folder in the given collection.
447: *
448: * @param folder TODO
449: * @param testSuiteFiles TODO
450: */
451: private void getTestSuiteFiles(File folder, Set testSuiteFiles) {
452: File[] files = folder.listFiles();
453: for (int i = 0; i < files.length; i++) {
454: File file = files[i];
455: if (file.isFile()) {
456: if (file.getName().endsWith(TESTSUITE_FILE_EXTENSION))
457: testSuiteFiles.add(file);
458: } else
459: getTestSuiteFiles(file, testSuiteFiles);
460: }
461: }
462:
463: /**
464: * Returns the temporary folder.
465: *
466: */
467: static public File getTempFolder() {
468: return _tempFolder;
469: }
470:
471: /**
472: * Returns the temporary folder.
473: *
474: */
475: static public File getFailFolder() {
476: return _failFolder;
477: }
478:
479: /**
480: * Increases the number of successful testcases.
481: *
482: */
483: public static void increaseSucceeded() {
484: ++_testCasesSucceeded;
485: }
486:
487: /**
488: * Increases the number of failed testcases.
489: *
490: */
491: public static void increaseFailed() {
492: ++_testCasesFailed;
493: }
494:
495: /**
496: * Increases the number of warning testcases.
497: *
498: */
499: public static void increaseWarning() {
500: ++_testCasesWarning;
501: }
502:
503: public static void increaseKnownBug() {
504: ++_testCasesKnownBug;
505: }
506:
507: public static void increaseFixed() {
508: ++_testCasesFixed;
509: }
510:
511: /**
512: * Returns the Output.
513: *
514: */
515: static Output getOutput() {
516: return _output;
517: }
518:
519: static String getGcc() {
520: return _gcc;
521: }
522:
523: static String getJVM() {
524: return _jvm;
525: }
526:
527: /**
528: * Returns whether comments should be written to output
529: *
530: */
531: static boolean getWriteComments() {
532: return _writeComments;
533: }
534:
535: /**
536: * Returns the runtime, where the Nice standard library is.
537: *
538: */
539: static String getRuntime() {
540: return _runtime;
541: }
542:
543: /**
544: @return the classloader used to run generated code.
545: */
546: static ClassLoader getClassLoader() {
547:
548: File[] dirs = { getTempFolder(), new File(getRuntime()) };
549: ClassLoader res = new nice.tools.util.DirectoryClassLoader(
550: dirs, null);
551: try {
552: nice.tools.util.JDK.setDefaultAssertionStatus(res, true);
553: } catch (java.lang.reflect.InvocationTargetException e) {
554: System.out.println("WARNING: could not enable assertions");
555: }
556: return res;
557: }
558:
559: /**
560: * Returns the total number of testcases.
561: *
562: */
563: static public int getTotalTestCases() {
564: return _testCasesSucceeded + _testCasesFailed
565: + _testCasesWarning + _testCasesKnownBug
566: + _testCasesFixed;
567: }
568:
569: /**
570: * Returns the number of succeded testcases.
571: *
572: */
573: static public int getTestCasesSucceeded() {
574: return _testCasesSucceeded;
575: }
576:
577: /**
578: * Returns the number of failed testcases.
579: *
580: */
581: static public int getTestCasesFailed() {
582: return _testCasesFailed;
583: }
584:
585: /**
586: * Returns the number of warning testcases.
587: *
588: */
589: static public int getTestCasesWarning() {
590: return _testCasesWarning;
591: }
592:
593: static public int getTestCasesKnownBug() {
594: return _testCasesKnownBug;
595: }
596:
597: static public int getTestCasesFixed() {
598: return _testCasesFixed;
599: }
600:
601: }
602:
603: // Local Variables:
604: // tab-width: 2
605: // indent-tabs-mode: t
606: // End:
|