From 00f916cb57da16826f8191b9ea792f55e40981fc Mon Sep 17 00:00:00 2001 From: ZhexinXiao Date: Wed, 2 May 2018 21:54:02 -0500 Subject: [PATCH 1/3] Enable user to test their project in parallel. Add "test in parallel" to the Drop-down menu and tool bar. Doing this by create a new class MyJUnitCore extended JunitCore which can run testsuite in parallel. It will be called in JUnitParallelTestRunner that extended JUnitTestRunner. JUnitParallelTestRunner is called by JUnitParallelTestManager that extended JUnitTestManager. All the test can pass. The files that changed or added modified: drjava/build.xml modified: drjava/src/edu/rice/cs/drjava/CommandLineTest.java modified: drjava/src/edu/rice/cs/drjava/DrJavaTestCase.java modified: drjava/src/edu/rice/cs/drjava/config/OptionConstants.java modified: drjava/src/edu/rice/cs/drjava/model/DJError.java modified: drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java modified: drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java modified: drjava/src/edu/rice/cs/drjava/model/junit/JUnitErrorModelTest.java modified: drjava/src/edu/rice/cs/drjava/model/junit/JUnitModel.java new file: drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java new file: drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestRunner.java modified: drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestManager.java modified: drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestRunner.java new file: drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java modified: drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java modified: drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVMRemoteI.java modified: drjava/src/edu/rice/cs/drjava/model/repl/newjvm/MainJVM.java modified: drjava/src/edu/rice/cs/drjava/ui/ErrorPanel.java modified: drjava/src/edu/rice/cs/drjava/ui/JUnitPanel.java modified: drjava/src/edu/rice/cs/drjava/ui/MainFrame.java modified: drjava/src/edu/rice/cs/drjava/ui/coverage/CoverageFrame.java modified: drjava/src/edu/rice/cs/util/LogTest.java --- drjava/build.xml | 104 +++- .../edu/rice/cs/drjava/CommandLineTest.java | 2 +- .../edu/rice/cs/drjava/DrJavaTestCase.java | 2 +- .../cs/drjava/config/OptionConstants.java | 16 + .../src/edu/rice/cs/drjava/model/DJError.java | 14 +- .../cs/drjava/model/GlobalModelJUnitTest.java | 1 - .../drjava/model/junit/DefaultJUnitModel.java | 20 +- .../model/junit/JUnitErrorModelTest.java | 3 +- .../cs/drjava/model/junit/JUnitModel.java | 5 + .../model/junit/JUnitParallelTestManager.java | 533 ++++++++++++++++++ .../model/junit/JUnitParallelTestRunner.java | 142 +++++ .../drjava/model/junit/JUnitTestManager.java | 38 +- .../drjava/model/junit/JUnitTestRunner.java | 10 +- .../cs/drjava/model/junit/MyJUnitCore.java | 46 ++ .../model/repl/newjvm/InterpreterJVM.java | 12 +- .../repl/newjvm/InterpreterJVMRemoteI.java | 2 +- .../cs/drjava/model/repl/newjvm/MainJVM.java | 12 +- .../src/edu/rice/cs/drjava/ui/ErrorPanel.java | 4 + .../src/edu/rice/cs/drjava/ui/JUnitPanel.java | 14 + .../src/edu/rice/cs/drjava/ui/MainFrame.java | 108 +++- .../cs/drjava/ui/coverage/CoverageFrame.java | 2 +- drjava/src/edu/rice/cs/util/LogTest.java | 14 +- 22 files changed, 1038 insertions(+), 66 deletions(-) create mode 100644 drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java create mode 100644 drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestRunner.java create mode 100644 drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java diff --git a/drjava/build.xml b/drjava/build.xml index 15c8ff541..b1c6b0cc8 100644 --- a/drjava/build.xml +++ b/drjava/build.xml @@ -61,13 +61,13 @@ - + - - + + - + @@ -345,7 +345,19 @@ - + + + + + + + + + + + + + @@ -425,8 +437,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -463,6 +517,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drjava/src/edu/rice/cs/drjava/CommandLineTest.java b/drjava/src/edu/rice/cs/drjava/CommandLineTest.java index 189befb74..6182d11df 100644 --- a/drjava/src/edu/rice/cs/drjava/CommandLineTest.java +++ b/drjava/src/edu/rice/cs/drjava/CommandLineTest.java @@ -435,4 +435,4 @@ private void checkFile(File relativeFile, String funnyName) // Close this doc to clean up after ourselves for the next check. _mf.getModel().closeFile(doc); } -} +} \ No newline at end of file diff --git a/drjava/src/edu/rice/cs/drjava/DrJavaTestCase.java b/drjava/src/edu/rice/cs/drjava/DrJavaTestCase.java index 72d4deb95..34a9d326a 100644 --- a/drjava/src/edu/rice/cs/drjava/DrJavaTestCase.java +++ b/drjava/src/edu/rice/cs/drjava/DrJavaTestCase.java @@ -114,4 +114,4 @@ public void run() { }); Utilities.clearEventQueue(); // ensure that all listener actions triggered by this document update have completed } -} +} \ No newline at end of file diff --git a/drjava/src/edu/rice/cs/drjava/config/OptionConstants.java b/drjava/src/edu/rice/cs/drjava/config/OptionConstants.java index bcb8ae1f3..0a9f13ad2 100644 --- a/drjava/src/edu/rice/cs/drjava/config/OptionConstants.java +++ b/drjava/src/edu/rice/cs/drjava/config/OptionConstants.java @@ -431,10 +431,16 @@ public static Vector vector(KeyStroke... ks) { public static final VectorOption KEY_COMPILE_PROJECT = new VectorOption("key.compile.project", new KeyStrokeOption("",null), to.vector()); + /** The key binding for testing a project in parallel. */ + public static final VectorOption KEY_JUNIT_PROJECT_PARALLEL = + new VectorOption("key.junit.project", new KeyStrokeOption("",null), to.vector()); + + /** The key binding for testing a project. */ public static final VectorOption KEY_JUNIT_PROJECT = new VectorOption("key.junit.project", new KeyStrokeOption("",null), to.vector()); + /** The key binding for running a project. */ public static final VectorOption KEY_RUN_PROJECT = new VectorOption("key.run.project", new KeyStrokeOption("",null), to.vector()); @@ -764,12 +770,20 @@ public static Vector vector(KeyStroke... ks) { new VectorOption("key.test", new KeyStrokeOption("",null), to.vector(KeyStroke.getKeyStroke(KeyEvent.VK_T, MASK|SHIFT_MASK))); + /** The key binding for testing the current document in parallel. */ + public static final VectorOption KEY_TEST_PARALLEL = + new VectorOption("key.reset.interactions", new KeyStrokeOption("",null), to.vector()); + /** The key binding for testing all open JUnit test cases. */ public static final VectorOption KEY_TEST_ALL = new VectorOption("key.test.all", new KeyStrokeOption("",null), to.vector(KeyStroke.getKeyStroke(KeyEvent.VK_T, MASK))); + /** The key binding for testing all open JUnit test cases in parallel. */ + public static final VectorOption KEY_TEST_ALL_PARALLEL = + new VectorOption("key.reset.interactions", new KeyStrokeOption("",null), to.vector()); + /** The key binding for generating javadoc for all documents */ public static final VectorOption KEY_JAVADOC_ALL = @@ -807,6 +821,8 @@ public static Vector vector(KeyStroke... ks) { public static final VectorOption KEY_RESET_INTERACTIONS = new VectorOption("key.reset.interactions", new KeyStrokeOption("",null), to.vector()); + + /** The key binding for viewing the interactions classpath. */ public static final VectorOption KEY_VIEW_INTERACTIONS_CLASSPATH = new VectorOption("key.view.interactions.classpath", new KeyStrokeOption("",null), to.vector()); diff --git a/drjava/src/edu/rice/cs/drjava/model/DJError.java b/drjava/src/edu/rice/cs/drjava/model/DJError.java index 06f5964c6..b78c4cabb 100644 --- a/drjava/src/edu/rice/cs/drjava/model/DJError.java +++ b/drjava/src/edu/rice/cs/drjava/model/DJError.java @@ -40,6 +40,7 @@ import java.io.Serializable; import edu.rice.cs.util.FileOps; +import edu.rice.cs.util.Log; import edu.rice.cs.util.UnexpectedException; @@ -48,6 +49,10 @@ * @version $Id$ */ public class DJError implements Comparable, Serializable { + + /** Debugging log. */ + public static Log _log = new Log("DJError.txt", false); + private volatile File _file; /** zero-based line number. */ @@ -78,6 +83,9 @@ public DJError(File file, int lineNumber, int startColumn, String message, boole _startColumn = startColumn; _message = message; _isWarning = isWarning; + //TODO + _log.log("_lineNumber= "+_lineNumber); + _log.log("_file= "+_file); if (lineNumber < 0) _noLocation = true; } @@ -127,7 +135,11 @@ public String fileName() { /** Sets the line number. * @param ln line number */ - public void setLineNumber(int ln) { _lineNumber = ln; } + public void setLineNumber(int ln) { + //TODO + _log.log("in setLineNumber _lineNumber= "+_lineNumber); + _lineNumber = ln; + } /** Gets the column where the error begins. * @return the starting column diff --git a/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java b/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java index 4dc11b328..f6c327453 100644 --- a/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java +++ b/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java @@ -937,4 +937,3 @@ public void testJUnit4TwoMethod1Test_NOJOIN() throws Exception { _log.log("testJUnit4TwoMethod1Test completed"); } } - diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java b/drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java index c39dfc762..576aeef96 100644 --- a/drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java +++ b/drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java @@ -84,11 +84,15 @@ * @version $Id$ */ public class DefaultJUnitModel implements JUnitModel, JUnitModelCallback { + + /**Set whether we should run the test in parallel, whenever use it, set it back to false*/ + public static boolean runTestParallel=false; private CoverageMetadata coverageMetadata = new CoverageMetadata(false, ""); /** log for use in debugging */ - private static Log _log = new Log("GlobalModel.txt", false); + //needtodo + private static Log _log = new Log("DefaultJUnitModel.txt", true); /** Manages listeners to this model. */ private final JUnitEventNotifier _notifier = new JUnitEventNotifier(); @@ -136,6 +140,11 @@ public DefaultJUnitModel(MainJVM jvm, CompilerModel compilerModel, SingleDisplay //-------------------------- Field Setters --------------------------------// + public void setRunTestParallel(boolean testParallel) { + _log.log("setRunTestParallel= "+testParallel); + runTestParallel=testParallel; + } + public void setCoverage(boolean coverage, String outdirPath) { this.coverageMetadata = new CoverageMetadata(coverage, outdirPath); } @@ -344,7 +353,8 @@ private void _rawJUnitOpenDefDocs(List lod, final boole File sourceDir = (buildDir == FileOps.NULL_FILE) ? classFileDir : new File(IOUtil.attemptCanonicalFile(sourceRoot), packagePath); - + _log.log("classFileDir= " + classFileDir + " sourceDir= " + sourceDir + " sourceRoot= "+sourceRoot); + _log.log("classDirsAndRoots= " + classDirsAndRoots); if (! classDirsAndRoots.containsKey(classFileDir)) { classDirsAndRoots.put(classFileDir, sourceDir); _log.log("Adding " + classFileDir + " with source root " + sourceRoot + " to list of class directories"); @@ -429,7 +439,7 @@ public void visitEnd() { } /** The canonical pathname for the file (including the file name) */ String javaSourceFileName = getCanonicalPath(rootDir) + File.separator + sourceName.value(); - + _log.log("javaSourceFileName= " + javaSourceFileName); // System.err.println("Full java source fileName = " + javaSourceFileName); /* The index in fileName of the dot preceding the extension ".java", ".dj", ".dj0*, ".dj1", or ".dj2" */ @@ -500,7 +510,9 @@ public void run() { try { _notifyJUnitStarted(); // The false return value could be changed to an exception. - boolean testsPresent = _jvm.runTestSuite(); + _log.log("runTestParallel= "+runTestParallel); + boolean testsPresent = _jvm.runTestSuite(runTestParallel); + runTestParallel=false; if (!testsPresent) { throw new RemoteException("No unit test classes were passed to the slave JVM"); } diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitErrorModelTest.java b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitErrorModelTest.java index 781fd7e09..7c087706f 100644 --- a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitErrorModelTest.java +++ b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitErrorModelTest.java @@ -426,5 +426,4 @@ public void run() { debug.logEnd(); _log.log("testErrorInSuperClass_NOJOIN complete"); } -} - +} \ No newline at end of file diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitModel.java b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitModel.java index dc4387266..dc7028003 100644 --- a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitModel.java +++ b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitModel.java @@ -43,8 +43,13 @@ public interface JUnitModel { + //-------------------------- Field Setters --------------------------------// + /** @param runTestParallel true if we run the test in parallel + */ + public void setRunTestParallel(boolean testParallel); + /** @param c true if a coverage report is desired; false otherwise * @param p value to set the outdir path to */ diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java new file mode 100644 index 000000000..f5cf785dc --- /dev/null +++ b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java @@ -0,0 +1,533 @@ +/*BEGIN_COPYRIGHT_BLOCK + * + * Copyright (c) 2001-2017, JavaPLT group at Rice University (drjava@rice.edu) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER OR + * CONTRIBUTORS 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. + * + * This software is Open Source Initiative approved Open Source Software. + * Open Source Initative Approved is a trademark of the Open Source Initiative. + * + * This file is part of DrJava. Download the current version of this project + * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/ + * + * END_COPYRIGHT_BLOCK*/ + +package edu.rice.cs.drjava.model.junit; + +import java.io.File; +import java.io.InputStream; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Vector; +import java.util.concurrent.TimeUnit; +import java.util.Enumeration; +import java.util.Arrays; + +import edu.rice.cs.drjava.model.coverage.CoverageMetadata; +import edu.rice.cs.drjava.model.coverage.ReportGenerator; + +import edu.rice.cs.drjava.model.repl.newjvm.ClassPathManager; + +import edu.rice.cs.util.Log; +import edu.rice.cs.util.UnexpectedException; +import edu.rice.cs.util.classloader.ClassFileError; + +import edu.rice.cs.plt.io.IOUtil; +import edu.rice.cs.plt.tuple.Pair; +import edu.rice.cs.plt.iter.IterUtil; + +import edu.rice.cs.drjava.model.coverage.JacocoClassLoader; +import edu.rice.cs.plt.reflect.EmptyClassLoader; + +import static edu.rice.cs.plt.debug.DebugUtil.error; + +import junit.framework.JUnit4TestAdapter; + +import junit.framework.AssertionFailedError; + +import junit.framework.Test; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import junit.framework.TestFailure; +import junit.framework.JUnit4TestCaseFacade; + +import org.jacoco.core.analysis.Analyzer; +import org.jacoco.core.analysis.CoverageBuilder; +import org.jacoco.core.analysis.IBundleCoverage; +import org.jacoco.core.data.ExecutionDataStore; +import org.jacoco.core.data.SessionInfoStore; + +import org.jacoco.core.instr.Instrumenter; +import org.jacoco.core.runtime.IRuntime; +import org.jacoco.core.runtime.LoggerRuntime; +import org.jacoco.core.runtime.RuntimeData; +import org.junit.experimental.ParallelComputer; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; + +/** Runs in the InterpreterJVM. Runs tests given a classname and formats the results into a (serializable) array of + * JUnitError that can be passed back to the MainJVM. + * @version $Id$ + */ +public class JUnitParallelTestManager extends JUnitTestManager{ + public static Log _log = new Log("JUnitParallelTestManager.txt", true); + + + + /** The current testRunner; initially null. Each test suite requires a new runner. */ + private JUnitParallelTestRunner _testRunner; + /** class of of test cases, used to run test case in parallel*/ + private Vector> cls=null; + + + + /** Standard constructor + * @param jmc a JUnitModelCallback + * @param loaderFactory factory to create class loaders + */ + public JUnitParallelTestManager(JUnitModelCallback jmc, ClassPathManager loaderFactory) { + super(jmc,loaderFactory); + } + + /** Find the test classes among the given classNames and accumulate them in + * TestSuite for junit. Returns null if a test suite is already pending. + * @param classNames the (fully qualified) class names that are test class candidates + * @param files Java File objects for the source files corresponding to classNames + * @param coverageMetadata metadata to be used to generate the coverage report + * @return list of test class names + */ + @SuppressWarnings({"unchecked","rawtypes"}) + public List findTestClasses(final List classNames, final List files, + final CoverageMetadata coverageMetadata) { + + _log.log("findTestClasses(" + classNames + ", " + files + ", " + coverageMetadata + ") called"); + boolean doCoverage = coverageMetadata.getFlag(); + + // Set up the loader + final ClassLoader defaultLoader = JUnitParallelTestManager.class.getClassLoader(); + final ClassLoader loader; + if (! doCoverage) loader = _classPathManager.value(defaultLoader); + else { + // create a Jacoco runtime, output directory, report descriptors, and loader + _coverageOutdir = coverageMetadata.getOutdirPath(); + _runtime = new LoggerRuntime(); + _myData = new RuntimeData(); + loader = new JacocoClassLoader(_classPathManager.getClassPath(), new Instrumenter(_runtime), defaultLoader); + _nonTestClassNames = new ArrayList(classNames.size()); + try { _runtime.startup(_myData); } + catch (Exception e) { + _log.log("In code coverage startup, throwing the wrapped exception " + e); + throw new UnexpectedException(e); + } + } + + if (_testClassNames != null && ! _testClassNames.isEmpty()) + throw new IllegalStateException("Test suite is still pending!"); + + _log.log("Preparing to run test cases"); + _testRunner = makeRunner(loader); + + _testClassNames = new ArrayList(); + _testFiles = new ArrayList(); + _nonTestClassNames = new ArrayList(classNames.size()); + _suite = new TestSuite(); + cls=new Vector>(); + // Assemble test suite (as _suite) and return list of test class names + for (Pair pair : IterUtil.zip(classNames, files)) { + String cName = pair.first(); + try { + Class possibleTest = _testRunner.loadPossibleTest(cName); + _log.log("Exploring possibleTest " + possibleTest); + if (_isJUnitTest(possibleTest)) { + _testClassNames.add(cName); + _testFiles.add(pair.second()); + Test test = new JUnit4TestAdapter(possibleTest); + cls.add(possibleTest); + _suite.addTest(test); + _log.log("Adding test " + test + " to test suite"); + } else { // cName is a program class that is not a test class + _nonTestClassNames.add(cName); + _log.log("adding " + cName + " to nonTestClassNames"); + } + } + catch (ClassNotFoundException e) { error.log(e); } + catch(LinkageError e) { + //debug.log(e); + String path = IOUtil.attemptAbsoluteFile(pair.second()).getPath(); + _jmc.classFileError(new ClassFileError(cName, path, e)); + } + } + +// debug.logEnd("result", _testClassNames); + _log.log("accumulated non test class names: " + _nonTestClassNames); + _log.log("returning: " + _testClassNames); + + return _testClassNames; + } + + /** Runs the pending test suite set up by the preceding call to findTestClasses. Runs in a single auxiliary thread, + * so no need for explicit synchronization. + * @param runTestParallel set whether we should run the test in parallel + * @return false if no test suite (even an empty one) has been set up + */ + public boolean runTestSuite(Boolean runTestParallel) { + + _log.log("runTestSuite() called"); + + if (_testClassNames == null || _testClassNames.isEmpty()) { + _finalResult = new JUnitResultTuple(false, null); + return false; + } + Map> lineColors = null; + _finalResult = new JUnitResultTuple(true, null); + +// _log.log("runTestSuite() in SlaveJVM called"); + + /* Declare fault array for amalgamating errors and failures */ + JUnitError[] faults = new JUnitError[0]; + try { + _log.log("Calling _testRunner.runSuite(" + _suite + ")"); + + + //This is the result of runSuite + Result result=null; + int faultCount=0; + long startTime = System.nanoTime(); + long estimatedTime = System.nanoTime() - startTime; + _log.log("isParallel= "+runTestParallel); + if (runTestParallel == true) { + @SuppressWarnings("rawtypes") + Class[] clsArray = new Class[cls.size()]; + cls.copyInto(clsArray); + _testRunner.setCountTestCases(_suite); + startTime = System.nanoTime(); + result = _testRunner.runClass(clsArray); + estimatedTime = System.nanoTime() - startTime; + long Second=TimeUnit.SECONDS.convert(estimatedTime, TimeUnit.NANOSECONDS); + _log.log("when testing in parallel, testing time is :" + estimatedTime +" in nanoseconds"); + _log.log("when testing in parallel, testing time is :" + Second +" in second"); + + faultCount = result.getFailureCount(); + _log.log("faultCount= " + faultCount); + if (faultCount > 0) { + + faults = new JUnitError[faultCount]; + List failure = result.getFailures(); + int i = 0; + for (Failure failureIterator : failure) { + // TestFailure error = failure. + _log.log("failureIterator.getDescription() is " + failureIterator.getDescription()); + String testMethodName = failureIterator.getDescription().getMethodName(); + _log.log("failureIterator.getDescription().getTestClass() is " + + failureIterator.getDescription().getTestClass()); + Test test = new JUnit4TestAdapter(failureIterator.getDescription().getTestClass()); + _log.log("test is " + test); + _log.log("testName is " + testMethodName); + TestFailure testFailure = new TestFailure(test, failureIterator.getException()); + faults[i] = _makeJUnitError(testFailure, _testClassNames, true, _testFiles, testMethodName); + i++; + } + + } + + } + else { + + startTime = System.nanoTime(); + TestResult testResult = _testRunner.runSuite(_suite); + estimatedTime = System.nanoTime() - startTime; + long Second=TimeUnit.SECONDS.convert(estimatedTime, TimeUnit.NANOSECONDS); + _log.log("when testing in sequential, testing time is :" + estimatedTime +" in nanoseconds"); + _log.log("when testing in sequential, testing time is :" + Second +" in second"); + /* A fault is either an error or a failure. */ + faultCount = testResult.errorCount() + testResult.failureCount(); + if (faultCount > 0) { + + /* + * NOTE: TestFailure, a JUnit class, is misnamed; it should have been called + * TestFault with TestFailure and TestError as disjoint subtypes (e.g., classes) + */ + faults = new JUnitError[faultCount]; + Enumeration failures = testResult.failures(); + Enumeration errors = testResult.errors(); + + int i = 0; + + // faults should be called faults! and makeJUnitError should be makeJUnitFault! + while (errors.hasMoreElements()) { + TestFailure error = errors.nextElement(); + faults[i] = _makeJUnitError(error, _testClassNames, true, _testFiles); + i++; + } + + while (failures.hasMoreElements()) { + TestFailure failure = failures.nextElement(); + faults[i] = _makeJUnitError(failure, _testClassNames, false, _testFiles); + i++; + } + } + } + + + _log.log("Testing doCoverage"); + + if (_runtime != null) { /* doCoverage was true */ + _log.log("Analyzing coverage data for " + _nonTestClassNames); + + /* Collect session info (including which code was executed) */ + final ExecutionDataStore _executionDataStore = new ExecutionDataStore(); + final SessionInfoStore sessionInfos = new SessionInfoStore(); + _myData.collect(_executionDataStore, sessionInfos, false); + _log.log("Collected coverage information"); + _runtime.shutdown(); + + /** Together with the original class definitions we can calculate coverage information. */ + final CoverageBuilder coverageBuilder = new CoverageBuilder(); + final Analyzer analyzer = new Analyzer(_executionDataStore, coverageBuilder); + URLClassLoader urlCL = newURLLoader(); + + String cName = null; + try { + for (int j = 0; j < _nonTestClassNames.size(); j++) { + cName = _nonTestClassNames.get(j); + InputStream is = urlCL.getResource(cName + ".class").openStream(); + _log.log("Constructed InputStream " + is + " for class " + cName); + analyzer.analyzeClass(is, cName); + } + } catch(Exception e) { + throw new UnexpectedException(e, "Coverage analysis threw this exception while processing class " + cName); + } + + /* Run the structure analyzer on the project source folder to build up the coverage model. In flat file + * mode, only the first source directory (if there are multiple source directories) is analyzed. TODO: + * extend this analysis to all source directories for the open classes in flat file mode. + */ + + _log.log("Generating test coverage"); + IBundleCoverage bundleCoverage = coverageBuilder.getBundle("Coverage Summary"); + ReportGenerator rg = new ReportGenerator(_coverageOutdir, coverageBuilder); + _log.log("Determining project root"); + _log.log("getProjectCP() = " + _classPathManager.getProjectFilesCP()); + File f = _classPathManager.getProjectFilesCP().iterator().next(); + if (! f.exists()) _log.log("****** Project root does not exist!"); + _log.log("Creating coverage report for code base rooted at " + f); + rg.createReport(bundleCoverage, _executionDataStore, sessionInfos, f); + lineColors = rg.getAllLineColors(); + _finalResult = new JUnitResultTuple(true, lineColors); + + } else { + _log.log("runtime was null"); + } + /* Reset the runtime */ + _runtime = null; + _reset(); + _jmc.testSuiteEnded(faults); + } + //TODO change error of line + catch (Exception e) { + faults = new JUnitError[] { + new JUnitError(null, -1, -1, e.getMessage(), false, "", "", e.toString(), e.getStackTrace()) + }; + _log.log("Slave JVM: testSuite ended with faults:" + Arrays.toString(faults)); + _reset(); + _jmc.testSuiteEnded(faults); + } + + _log.log("Exiting runTestSuite()"); + return _finalResult.getRetval(); + } + + private void _reset() { + _suite = null; + _testClassNames = null; + _testFiles = null; + _log.log("test manager state reset"); + } + + + /** Constructs a new JUnitError from a TestFailure + * @param failure A given TestFailure + * @param classNames The classes that were used for this test suite + * @param isError The passed TestFailure may signify either an error or a failure + * @param files The files that were used for this test suite + * @param testMethodName name of tested method + * @return JUnitError + */ + private JUnitError _makeJUnitError(TestFailure failure, List classNames, boolean isError, List files,String testMethodName) { + + _log.log("_makeJUnitError called with failure " + failure + " failedTest = " + failure.failedTest()); + Test failedTest = failure.failedTest(); + _log.log("failedTest " + failedTest); + _log.log("failure.exceptionMessage " + failure.exceptionMessage()); + _log.log("failure.toString " + failure.toString()); + + String testString = failure.toString(); + + /** junit can return a string in two different formats; we parse both formats, and then decide which one to use. */ + + //needtodo + String className=failedTest.toString(); + String testName=testMethodName; + _log.log("className " + className +" testName "+testName); + + String classNameAndTest = className + "." + testName; +// _log.log("classNameAndTest = " + classNameAndTest); + String exception = failure.thrownException().toString(); + StackTraceElement[] stackTrace = failure.thrownException().getStackTrace(); + + /* Check to see if the class and test name appear directly in the stack trace. If + * they don't, then we'll have to do additional work to find the line number. Additionally, + * if the exception occured in a subclass of the test class, we'll need to adjust our conception + * of the class name. + */ + StringBuilder sb = new StringBuilder(); + sb.append(exception); + sb.append('\n'); + for(StackTraceElement s: stackTrace) { + sb.append("\tat "); + sb.append(s); + } + String combined = sb.toString(); + int lineNum = -1; + _log.log("start to cal lineNum"); + + if (combined.indexOf(classNameAndTest) == -1) { + /* get the stack trace of the junit error */ + String trace = failure.trace(); + _log.log("failure.trace is " +trace); + /* knock off the first line of the stack trace. + * now the string will look like + * at my.package.class(file.java:line) + * at other.package.class(anotherfile.java:line) + * etc... + */ + trace = trace.substring(trace.indexOf('\n')+1); + if (trace.trim().length()>0) { + while (trace.indexOf("junit.framework") != -1 && + trace.indexOf("junit.framework") < trace.indexOf("(")) { + /* the format of the trace will have "at junit.framework.Assert..." + * and "junit.framework.TestCase..." + * on each line until the line of the actual source file. + * if the exception was thrown from the test case (so the test failed + * without going through assert), then the source file will be on + * the first line of the stack trace + */ + trace = trace.substring(trace.indexOf('\n') + 1); + } + + _log.log("trace after junit.framework.Assert is " +trace); + _log.log("testName is " +testName); + trace = trace.substring(trace.indexOf(testName)+2); + trace = trace.substring(0, trace.indexOf(')')); + + _log.log("trace after substring " +trace); + // If the exception occurred in a subclass of the test class, then update our + // concept of the class and test name. Otherwise, we're only here to pick up the + // line number. + if (combined.indexOf(className) == -1) { + int dotPos = trace.lastIndexOf('.'); + if (dotPos!=-1) { + className = trace.substring(0,dotPos); + classNameAndTest = className + "." + testName; + } + } + + try { + _log.log("trace is " +trace); + lineNum = Integer.parseInt(trace.substring(trace.indexOf(':') + 1)) - 1; + } + catch (NumberFormatException e) { lineNum = 0; } // may be native method + } + } + _log.log("lineNum is " +lineNum); + if (lineNum < 0) { + lineNum = _lineNumber(combined, classNameAndTest); + } + +// if (lineNum > -1) _faultsWithPos++; + + String message = (isError) ? failure.thrownException().toString(): + failure.thrownException().getMessage(); + + boolean isFailure = (failure.thrownException() instanceof AssertionError || + failure.thrownException() instanceof AssertionFailedError) && + !classNameAndTest.equals("junit.framework.TestSuite$1.warning"); + +// for debugging +// try { +// File temp = File.createTempFile("asdf", "java", new File("/home/awulf")); +// FileWriter writer = new FileWriter(temp); +// writer.write("testString: " + testString + "\n"); +// writer.write("old className: " + className1 + "\n"); +// writer.write("new className: " + className2 + "\n"); +// writer.write("file: " + file + "\n"); +// writer.write("lineNum: " + lineNum + "\n"); +// writer.write("exception: " + exception + "\n"); +// writer.write("!isFailure: " + !isFailure + "\n"); +// writer.write("testName: " + testName + "\n"); +// writer.write("className: " + className + "\n"); +// writer.write("stackTrace: " + stackTrace + "\n"); +// writer.close(); +// } catch(IOException e) { +// +// } + + int indexOfClass = classNames.indexOf(className); + File file; + if (indexOfClass != -1) file = files.get(indexOfClass); + else file = _jmc.getFileForClassName(className); + + // if testClass contains no + + // a test didn't fail, we couldn't even open the test. + if (file == null) { + return new JUnitError(new File("nofile"), 0, 0, message, !isFailure, testName, className, exception, stackTrace); + } + + return new JUnitError(file, lineNum, 0, message, !isFailure, testName, className, exception, stackTrace); + } + + + + + + /** @param loader current template for the runner's class loader + * @return a fresh JUnitTestRunner with its own class loader instance. + */ + protected JUnitParallelTestRunner makeRunner(ClassLoader loader) { + return new JUnitParallelTestRunner(_jmc, loader); + } +} diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestRunner.java b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestRunner.java new file mode 100644 index 000000000..5aa6ec45a --- /dev/null +++ b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestRunner.java @@ -0,0 +1,142 @@ +/*BEGIN_COPYRIGHT_BLOCK + * + * Copyright (c) 2001-2017, JavaPLT group at Rice University (drjava@rice.edu) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER OR + * CONTRIBUTORS 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. + * + * This software is Open Source Initiative approved Open Source Software. + * Open Source Initative Approved is a trademark of the Open Source Initiative. + * + * This file is part of DrJava. Download the current version of this project + * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/ + * + * END_COPYRIGHT_BLOCK*/ + +package edu.rice.cs.drjava.model.junit; + +import junit.runner.*; +import junit.framework.*; + +import java.util.Vector; + +import org.junit.experimental.ParallelComputer; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.AllTests; +import org.junit.runners.Suite.SuiteClasses; + +import edu.rice.cs.util.Log; +import edu.rice.cs.util.UnexpectedException; + +/** DrJava's own testrunner. It updates the document in the JUnit pane as error and failure events are fired. + * These methods run inan auxiliary thread. + * @version $Id$ + */ +public class JUnitParallelTestRunner extends JUnitTestRunner { + + protected static final Log _log = new Log("JUnitParallelTestRunner.txt", false); + + protected Vector failedTest=new Vector(); + + + + /** Standard constructor. + * @param jmc a JUnitModelCallback + * @param loader class loader to use during testing + */ + public JUnitParallelTestRunner(JUnitModelCallback jmc, ClassLoader loader) { + super(jmc,loader); + } + + + //Result result= JUnitCore.runClasses(new ParallelComputer(true, true), clsArray); + //Result result = _testRunner.runSuite(_suite); + + //how many test cases will run + private int countTestCases=0; + /** + * This method will manually set the number of test cases(number of method) + * @param suite testSuite that contain all test cases + */ + public void setCountTestCases(TestSuite suite) + { + _log.log("getCountTestCases"); + + countTestCases=suite.countTestCases(); + //suite.run(_result); + } + + /** + * method to run class + * + * @param classes + * @return + */ + public synchronized Result runClass(Class... classes) { + _log.log("start run in runclass,countTestCases= " + countTestCases); + + // Reset all bookkeeping + _errorCount = 0; + _failureCount = 0; + + // Run the test + _result = new TestResult(); + _result.addListener(this); + _jmc.testSuiteStarted(countTestCases); + MyJUnitCore myJunitCore = new MyJUnitCore(); + return myJunitCore.parallelRunClasses(new RunListener() { + public void testStarted(Description description) { + _log.log(" in testStarted " + description.getMethodName()); + // The parameter testName of testStarted is the form of testMethod(testClass) + _jmc.testStarted(description.getMethodName() + "(" + description.getClassName() + ")"); + } + + public void testFinished(Description description) { + _log.log(" in testEnded " + description.getMethodName()); + String testName = description.getClassName() + ":" + description.getMethodName(); + boolean success; + if (failedTest.indexOf(testName) == -1) + success = true; + else + success = false; + // The parameter testName of testStarted is the form of testMethod(testClass) + _jmc.testEnded(description.getMethodName() + "(" + description.getClassName() + ")", success, false); + } + + public void testFailure(Failure failure) { + _log.log(" in testFailed " + failure.getMessage()); + String testName = failure.getDescription().getClassName() + ":" + + failure.getDescription().getMethodName(); + failedTest.add(testName); + } + + }, classes); + } + +} diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestManager.java b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestManager.java index 297d1bbbb..edb9a5ff1 100644 --- a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestManager.java +++ b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestManager.java @@ -98,32 +98,33 @@ */ public class JUnitTestManager { - protected static final Log _log = new Log("GlobalModel.txt", false); + /** log for use in debugging */ + protected static final Log _log = new Log("JUnitTestManager.txt", false); /** The interface to the master JVM via RMI. */ - private final JUnitModelCallback _jmc; + protected final JUnitModelCallback _jmc; /** A factory producing a ClassLoader for tests with the given parent */ - private final ClassPathManager _classPathManager; + protected final ClassPathManager _classPathManager; /** The current testRunner; initially null. Each test suite requires a new runner. */ - private JUnitTestRunner _testRunner; + protected JUnitTestRunner _testRunner; /** The accumulated test suite; null if no test is pending. */ - private TestSuite _suite = null; + protected TestSuite _suite = null; /** The accumulated list of names of TestCase classes; null if no test is pending. */ - private List _testClassNames = null; + protected List _testClassNames = null; /** The list of files corresponding to testClassNames; null if no test is pending. */ - private List _testFiles = null; + protected List _testFiles = null; // Create and initialize fields for JaCoCo - private String _coverageOutdir = null; - private IRuntime _runtime = null; - private RuntimeData _myData = null; - private List _nonTestClassNames = null; - private JUnitResultTuple _finalResult = new JUnitResultTuple(false, null); + protected String _coverageOutdir = null; + protected IRuntime _runtime = null; + protected RuntimeData _myData = null; + protected List _nonTestClassNames = null; + protected JUnitResultTuple _finalResult = new JUnitResultTuple(false, null); /** Standard constructor * @param jmc a JUnitModelCallback @@ -140,7 +141,7 @@ public JUnitTestManager(JUnitModelCallback jmc, ClassPathManager loaderFactory) /** Used to load class files in the analysis phase of code coverage * @return URLClassLoader with DrJava classpath */ - private URLClassLoader newURLLoader() { + protected URLClassLoader newURLLoader() { List urls = new LinkedList(); for (File f : _classPathManager.getClassPath()) { try { urls.add(f.toURI().toURL()); } @@ -357,7 +358,7 @@ private void _reset() { * @param c the class to check * @return true iff the given class is an instance of junit.framework.Test */ - private boolean _isJUnitTest(Class c) { + protected boolean _isJUnitTest(Class c) { _log.log("Testing class " + c + " to determine if it is a JUnit test class"); // test first for JUnit 4 annotated test methods @@ -378,10 +379,11 @@ private boolean _isJUnitTest(Class c) { * @param files The files that were used for this test suite * @return JUnitError */ - private JUnitError _makeJUnitError(TestFailure failure, List classNames, boolean isError, List files) { + protected JUnitError _makeJUnitError(TestFailure failure, List classNames, boolean isError, List files) { // _log.log("_makeJUnitError called with failure " + failure + " failedTest = " + failure.failedTest()); Test failedTest = failure.failedTest(); + _log.log("failedTest " + failedTest); String testName; if (failedTest instanceof JUnit4TestCaseFacade) { testName = ((JUnit4TestCaseFacade) failedTest).toString(); @@ -394,7 +396,7 @@ private JUnitError _makeJUnitError(TestFailure failure, List classNames, int secondIndex = testString.indexOf(')'); /** junit can return a string in two different formats; we parse both formats, and then decide which one to use. */ - + _log.log("testString " + testString +" testName "+testName); String className; if (firstIndex != secondIndex) className = testString.substring(firstIndex, secondIndex); @@ -515,7 +517,7 @@ private JUnitError _makeJUnitError(TestFailure failure, List classNames, * @param classname class in which stack trace was generated * @return the line number */ - private int _lineNumber(String sw, String classname) { + protected int _lineNumber(String sw, String classname) { // TODO: use stack trace elements to find line number int lineNum; int idxClassname = sw.indexOf(classname); @@ -539,7 +541,7 @@ private int _lineNumber(String sw, String classname) { /** @param loader current template for the runner's class loader * @return a fresh JUnitTestRunner with its own class loader instance. */ - private JUnitTestRunner makeRunner(ClassLoader loader) { + protected JUnitTestRunner makeRunner(ClassLoader loader) { return new JUnitTestRunner(_jmc, loader); } } diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestRunner.java b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestRunner.java index d6ca49fe2..6cbf7c258 100644 --- a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestRunner.java +++ b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitTestRunner.java @@ -51,19 +51,19 @@ public class JUnitTestRunner extends BaseTestRunner { protected static final Log _log = new Log("JUnitTestManager.txt", false); /** Receives updates on the test suite's progress. */ - private JUnitModelCallback _jmc; + protected JUnitModelCallback _jmc; /** Class loader that uses DrJava's classpath. */ - private ClassLoader _loader; + protected ClassLoader _loader; /** The JUnit TestResult being accumulated. */ - private TestResult _result; + protected TestResult _result; /** The current number of errors in the result. */ - private int _errorCount; + protected int _errorCount; /** The current number of failures in the result. */ - private int _failureCount; + protected int _failureCount; diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java b/drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java new file mode 100644 index 000000000..f0a0f6743 --- /dev/null +++ b/drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java @@ -0,0 +1,46 @@ +package edu.rice.cs.drjava.model.junit; + +import org.junit.experimental.ParallelComputer; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; + +/** + * This class is used to run test in parallel. + * It uses ParallelComputer to parallel test method and test class + * @author zhexin + * + */ +public class MyJUnitCore extends JUnitCore { + + private final RunNotifier runNotifier = new RunNotifier(); + + /** + * method to run test classes in parallel + * @param listener listener for these test case + * @param classes test cases + * @return result of running these test case + */ + public Result parallelRunClasses(RunListener listener, Class[] classes) { + Runner runner = Request.classes(new ParallelComputer(true, true), classes).getRunner(); + Result result = new Result(); + RunListener resultListener = result.createListener(); + runNotifier.addFirstListener(resultListener); + runNotifier.addListener(listener); + try { + runNotifier.fireTestRunStarted(runner.getDescription()); + runner.run(runNotifier); + runNotifier.fireTestRunFinished(result); + } finally { + removeListener(listener); + removeListener(resultListener); + + } + return result; + + } + +} diff --git a/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java b/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java index a2c987e6f..97ad4c987 100644 --- a/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java +++ b/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java @@ -59,6 +59,7 @@ import edu.rice.cs.drjava.platform.PlatformFactory; import edu.rice.cs.drjava.model.junit.JUnitModelCallback; +import edu.rice.cs.drjava.model.junit.JUnitParallelTestManager; import edu.rice.cs.drjava.model.junit.JUnitTestManager; import edu.rice.cs.drjava.model.junit.JUnitError; import edu.rice.cs.drjava.model.junit.JUnitResultTuple; @@ -86,8 +87,8 @@ * @version $Id$ */ public class InterpreterJVM extends AbstractSlaveJVM implements InterpreterJVMRemoteI, JUnitModelCallback { - - public static final Log _log = new Log("GlobalModel.txt", false); + /** log for use in debugging */ + public static final Log _log = new Log("InterpreterJVM.txt", false); /** Singleton instance of this class. */ public static final InterpreterJVM ONLY = new InterpreterJVM(); @@ -110,7 +111,8 @@ public class InterpreterJVM extends AbstractSlaveJVM implements InterpreterJVMRe private final Object _stateLock = new Object(); /** Responsible for running JUnit tests in this JVM. */ - private final JUnitTestManager _junitTestManager; + private final JUnitParallelTestManager _junitTestManager; + /** Remote reference to the MainJVM class in DrJava's primary JVM. Assigned ONLY once. */ private volatile MainJVMRemoteI _mainJVM; @@ -122,7 +124,7 @@ private InterpreterJVM() { /* Important singleton objects embedded in an InterpreterJVM */ _classPathManager = new ClassPathManager(ReflectUtil.SYSTEM_CLASS_PATH); _interpreterLoader = _classPathManager.makeClassLoader(InterpreterJVM.class.getClassLoader()); - _junitTestManager = new JUnitTestManager(this, _classPathManager); + _junitTestManager = new JUnitParallelTestManager(this, _classPathManager); // set the thread context class loader, this way NextGen and Mint can use the interpreter's class loader Thread.currentThread().setContextClassLoader(_interpreterLoader); @@ -541,7 +543,7 @@ public List findTestClasses(List classNames, * and does not involve mutable local state. * @return false if no test suite is cached; true otherwise */ - public boolean runTestSuite() throws RemoteException { return _junitTestManager.runTestSuite(); } + public boolean runTestSuite(Boolean runTestParallel) throws RemoteException { return _junitTestManager.runTestSuite(runTestParallel); } /** Notifies Main JVM that JUnit has been invoked on a non TestCase class. Unsynchronized because it contains a * remote call and does not involve mutable local state. diff --git a/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVMRemoteI.java b/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVMRemoteI.java index 89cec0507..ce6059743 100644 --- a/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVMRemoteI.java +++ b/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVMRemoteI.java @@ -53,7 +53,7 @@ public interface InterpreterJVMRemoteI extends SlaveRemote { public List findTestClasses(List classNames, List files, CoverageMetadata coverageMetadata) throws RemoteException; - public boolean runTestSuite() throws RemoteException; + public boolean runTestSuite(Boolean runTestParallel) throws RemoteException; //public JUnitResultTuple getLastJUnitResult(); diff --git a/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/MainJVM.java b/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/MainJVM.java index f48c5f899..23b50fb91 100644 --- a/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/MainJVM.java +++ b/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/MainJVM.java @@ -58,6 +58,7 @@ import edu.rice.cs.util.ArgumentTokenizer; import edu.rice.cs.util.FileOps; +import edu.rice.cs.util.Log; import edu.rice.cs.util.UnexpectedException; import edu.rice.cs.plt.io.IOUtil; import edu.rice.cs.plt.iter.IterUtil; @@ -94,6 +95,9 @@ */ public class MainJVM extends AbstractMasterJVM implements MainJVMRemoteI { + /** Debugging log. */ + public static Log _log = new Log("MainJVM.txt", false); + /** Number of slave startup failures allowed before aborting the startup process. */ private static final int MAX_STARTUP_FAILURES = 3; @@ -479,13 +483,17 @@ public Option> findTestClasses(List classNames, /** Runs the JUnit test suite already cached in the Interpreter JVM. * Blocks until the remote JVM is available. + * @param runTestParallel Set whether we should run the test in parallel * @return {@code false} if no test suite is cached, the remote JVM is * unavailable, or an error occurs; true otherwise. */ - public boolean runTestSuite() { + public boolean runTestSuite(boolean runTestParallel) { InterpreterJVMRemoteI remote = _state.value().interpreter(true); if (remote == null) { return false; } - try { return remote.runTestSuite(); } + try { + _log.log("runTestParallel= "+runTestParallel); + return remote.runTestSuite(runTestParallel); + } catch (RemoteException e) { _handleRemoteException(e); return false; } } diff --git a/drjava/src/edu/rice/cs/drjava/ui/ErrorPanel.java b/drjava/src/edu/rice/cs/drjava/ui/ErrorPanel.java index a6c8386ea..c13907ebd 100644 --- a/drjava/src/edu/rice/cs/drjava/ui/ErrorPanel.java +++ b/drjava/src/edu/rice/cs/drjava/ui/ErrorPanel.java @@ -45,6 +45,7 @@ import edu.rice.cs.drjava.model.DJError; import edu.rice.cs.drjava.model.compiler.CompilerErrorModel; import edu.rice.cs.drjava.model.ClipboardHistoryModel; +import edu.rice.cs.util.Log; import edu.rice.cs.util.UnexpectedException; import edu.rice.cs.util.swing.HighlightManager; import edu.rice.cs.util.swing.BorderlessScrollPane; @@ -77,6 +78,8 @@ */ public abstract class ErrorPanel extends TabbedPanel implements OptionConstants { + /** Debugging log. */ + public static Log _log = new Log("ErrorPanel.txt", false); protected static final SimpleAttributeSet NORMAL_ATTRIBUTES = _getNormalAttributes(); protected static final SimpleAttributeSet BOLD_ATTRIBUTES = _getBoldAttributes(); @@ -668,6 +671,7 @@ protected void _insertErrors(ErrorDocument doc) throws BadLocationException { protected void _insertErrorText(DJError error, ErrorDocument doc) throws BadLocationException { // Show file and line number doc.append("File: ", BOLD_ATTRIBUTES); + //TODO String fileAndLineNumber = error.getFileMessage() + " [line: " + error.getLineMessage() + "]"; doc.append(fileAndLineNumber + "\n", NORMAL_ATTRIBUTES); diff --git a/drjava/src/edu/rice/cs/drjava/ui/JUnitPanel.java b/drjava/src/edu/rice/cs/drjava/ui/JUnitPanel.java index 77b0df183..237298b12 100644 --- a/drjava/src/edu/rice/cs/drjava/ui/JUnitPanel.java +++ b/drjava/src/edu/rice/cs/drjava/ui/JUnitPanel.java @@ -40,6 +40,7 @@ import edu.rice.cs.drjava.model.DJError; import edu.rice.cs.drjava.model.junit.JUnitError; import edu.rice.cs.drjava.model.junit.JUnitErrorModel; +import edu.rice.cs.util.Log; import edu.rice.cs.util.UnexpectedException; import edu.rice.cs.util.swing.BorderlessScrollPane; import edu.rice.cs.util.swing.RightClickMouseAdapter; @@ -59,6 +60,10 @@ * @version $Id$ */ public class JUnitPanel extends ErrorPanel { + + /** Debugging log. */ + public static Log _log = new Log("JUnitPanel.txt", false); + private static final String START_JUNIT_MSG = "Testing in progress. Please wait ...\n"; private static final String JUNIT_FINISHED_MSG = "All tests completed successfully.\n"; private static final String NO_TESTS_MSG = ""; @@ -254,11 +259,15 @@ private String _getClassFromName(String name) { * @param name the name of the test being run */ public void testStarted(String name) { + _log.log("testStarted name= " + name ); + if (name.indexOf('(') < 0) return; String testName = _getTestFromName(name); String className = _getClassFromName(name); String fullName = className + "." + testName; + _log.log(" fullName= " + fullName ); + if (fullName.equals(JUNIT_WARNING)) return; ErrorDocument doc = getErrorDocument(); try { @@ -290,6 +299,7 @@ public void testStarted(String name) { * @param causedError whether the test caused an error */ public void testEnded(String name, boolean wasSuccessful, boolean causedError) { + _log.log("testEnded(" + name + ", " + wasSuccessful + ", " + causedError + ")"); if (name.indexOf('(')<0) return; String testName = _getTestFromName(name); @@ -327,10 +337,14 @@ public void setJUnitInProgress() { /** Used to show that testing was unsuccessful. */ protected void _updateWithErrors() throws BadLocationException { + //DefaultStyledDocument doc = new DefaultStyledDocument(); ErrorDocument doc = getErrorDocument(); + _log.log("doc is "+doc.getText()); // _checkSync(doc); _updateWithErrors("test", "failed", doc); + _log.log("doc is "+doc.getText()); + } /** Gets the message indicating the number of errors and warnings.*/ diff --git a/drjava/src/edu/rice/cs/drjava/ui/MainFrame.java b/drjava/src/edu/rice/cs/drjava/ui/MainFrame.java index b8447e118..2890a20d7 100644 --- a/drjava/src/edu/rice/cs/drjava/ui/MainFrame.java +++ b/drjava/src/edu/rice/cs/drjava/ui/MainFrame.java @@ -91,6 +91,7 @@ import edu.rice.cs.drjava.model.definitions.NoSuchDocumentException; import edu.rice.cs.drjava.model.debug.*; import edu.rice.cs.drjava.model.repl.*; +import edu.rice.cs.drjava.model.repl.newjvm.MainJVM; import edu.rice.cs.drjava.model.javadoc.JavadocModel; import edu.rice.cs.drjava.ui.config.ConfigFrame; import edu.rice.cs.drjava.ui.coverage.CoverageFrame; @@ -131,6 +132,7 @@ import static edu.rice.cs.util.XMLConfig.XMLConfigException; import static edu.rice.cs.drjava.ui.MainFrameStatics.*; +import edu.rice.cs.drjava.model.junit.JUnitParallelTestManager; import edu.rice.cs.drjava.model.junit.JUnitResultTuple; /** DrJava's main window. */ @@ -779,9 +781,20 @@ public void actionPerformed(ActionEvent ae) { GUIAvailabilityListener.ComponentType.JUNIT, GUIAvailabilityListener.ComponentType.COMPILER, GUIAvailabilityListener.ComponentType.INTERACTIONS); } - public final void actionPerformed(ActionEvent ae) { _junitFolder(); } + public final void actionPerformed(ActionEvent ae) { _junitFolder(false); } }; + + /** Tests all the files in a folder in parallel. */ + private volatile AbstractAction _junitPararrelFolderAction = new AbstractAction("Test Folder in parallel") { + { _addGUIAvailabilityListener(this, + GUIAvailabilityListener.ComponentType.JUNIT, + GUIAvailabilityListener.ComponentType.COMPILER, + GUIAvailabilityListener.ComponentType.INTERACTIONS); } + public final void actionPerformed(ActionEvent ae) { _junitFolder(true); } + }; + + /** Saves the current document. */ private final Action _saveAction = new AbstractAction("Save") { public final void actionPerformed(ActionEvent ae) { _save(); } @@ -1021,7 +1034,19 @@ public void actionPerformed(ActionEvent ae) { GUIAvailabilityListener.ComponentType.INTERACTIONS); } public void actionPerformed(ActionEvent ae) { if (_mainSplit.getDividerLocation() > _mainSplit.getMaximumDividerLocation()) _mainSplit.resetToPreferredSizes(); - _junit(); + _junit(false); + } + }; + + /** Runs JUnit on the document in the definitions pane in parallel. */ + private volatile AbstractAction _junitActionParallel = new AbstractAction("Test Current Document in parallel") { + { _addGUIAvailabilityListener(this, // init + GUIAvailabilityListener.ComponentType.JUNIT, + GUIAvailabilityListener.ComponentType.COMPILER, + GUIAvailabilityListener.ComponentType.INTERACTIONS); } + public void actionPerformed(ActionEvent ae) { + if (_mainSplit.getDividerLocation() > _mainSplit.getMaximumDividerLocation()) _mainSplit.resetToPreferredSizes(); + _junit(true); } }; @@ -1033,12 +1058,28 @@ public void actionPerformed(ActionEvent ae) { GUIAvailabilityListener.ComponentType.INTERACTIONS); } public void actionPerformed(ActionEvent e) { if (_mainSplit.getDividerLocation() > _mainSplit.getMaximumDividerLocation()) _mainSplit.resetToPreferredSizes(); - _junitAll(); + _junitAll(false); _findReplace.updateFirstDocInSearch(); } }; + /** Runs JUnit over all open JUnit tests. */ + private volatile AbstractAction _junitAllActionParallel = new AbstractAction("Test All Documents in Parallel") { + { _addGUIAvailabilityListener(this, // init + GUIAvailabilityListener.ComponentType.JUNIT, + GUIAvailabilityListener.ComponentType.COMPILER, + GUIAvailabilityListener.ComponentType.INTERACTIONS); } + public void actionPerformed(ActionEvent e) { + if (_mainSplit.getDividerLocation() > _mainSplit.getMaximumDividerLocation()) _mainSplit.resetToPreferredSizes(); + _junitAll(true); + _findReplace.updateFirstDocInSearch(); + } + + }; + + + /** Runs JUnit over all open JUnit tests in the project directory. */ private volatile AbstractAction _junitProjectAction = new AbstractAction("Test Project") { { _addGUIAvailabilityListener(this, // init @@ -1048,7 +1089,21 @@ public void actionPerformed(ActionEvent e) { GUIAvailabilityListener.ComponentType.INTERACTIONS); } public void actionPerformed(ActionEvent e) { if (_mainSplit.getDividerLocation() > _mainSplit.getMaximumDividerLocation()) _mainSplit.resetToPreferredSizes(); - _junitProject(); + _junitProject(false); + _findReplace.updateFirstDocInSearch(); + } + }; + + /** Runs JUnit over all open JUnit tests in the project directory. */ + private volatile AbstractAction _junitProjectActionParallel = new AbstractAction("Test Project in Parallel") { + { _addGUIAvailabilityListener(this, // init + GUIAvailabilityListener.ComponentType.PROJECT, + GUIAvailabilityListener.ComponentType.JUNIT, + GUIAvailabilityListener.ComponentType.COMPILER, + GUIAvailabilityListener.ComponentType.INTERACTIONS); } + public void actionPerformed(ActionEvent e) { + if (_mainSplit.getDividerLocation() > _mainSplit.getMaximumDividerLocation()) _mainSplit.resetToPreferredSizes(); + _junitProject(true); _findReplace.updateFirstDocInSearch(); } }; @@ -5933,7 +5988,7 @@ private void _runApplet() { catch (IOException ioe) { MainFrameStatics.showIOError(MainFrame.this, ioe); } } - private void _junit() { + private void _junit(Boolean runTestParallel) { /* */ assert Utilities.TEST_MODE || EventQueue.isDispatchThread(); hourglassOn(); // turned off in junitStarted/nonTestCase/_junitInterrupted @@ -5944,12 +5999,16 @@ private void _junit() { // now also works with multiple documents // hourglassOn(); // moved into the prelude before this thread start - try { _model.getJUnitModel().junitDocs(_model.getDocumentNavigator().getSelectedDocuments()); } + try { + if(runTestParallel==true) + _model.getJUnitModel().setRunTestParallel(true); + _model.getJUnitModel().junitDocs(_model.getDocumentNavigator().getSelectedDocuments()); + } catch(UnexpectedException e) { _junitInterrupted(e); } catch(Exception e) { _junitInterrupted(new UnexpectedException(e)); } } - private void _junitFolder() { + private void _junitFolder(Boolean runTestParallel) { updateStatusField("Running Unit Tests in Current Folder"); hourglassOn(); // turned off in junitStarted/nonTestCase/_junitInterrupted // moved this back into the event thread to fix bug 2848696 @@ -5964,28 +6023,39 @@ private void _junitFolder() { for (OpenDefinitionsDocument doc: docs) { if (_model.getDocumentNavigator().isSelectedInGroup(doc)) l.add(doc); } - try { _model.getJUnitModel().junitDocs(l); } // hourglassOn executed by junitStarted() + try { + if(runTestParallel==true) + _model.getJUnitModel().setRunTestParallel(true); + _model.getJUnitModel().junitDocs(l); } // hourglassOn executed by junitStarted() catch(UnexpectedException e) { _junitInterrupted(e); } catch(Exception e) { _junitInterrupted(new UnexpectedException(e)); } } } /** Tests the documents in the project source tree. Assumes that DrJava is in project mode. */ - private void _junitProject() { + private void _junitProject(boolean runTestParallel) { updateStatusField("Running JUnit Tests in Project"); hourglassOn(); // turned off in junitStarted/nonTestCase/_junitInterrupted _guiAvailabilityNotifier.junitStarted(); // JUNIT and COMPILER - try { _model.getJUnitModel().junitProject(); } + try { + if(runTestParallel==true) + _model.getJUnitModel().setRunTestParallel(true); + _model.getJUnitModel().junitProject(); + } catch(UnexpectedException e) { _junitInterrupted(e); } catch(Exception e) { _junitInterrupted(new UnexpectedException(e)); } } /** Tests all open documents. */ - public void _junitAll() { + public void _junitAll(boolean runTestParallel) { updateStatusField("Running All Open Unit Tests"); hourglassOn(); // turned off in junitStarted/nonTestCase/_junitInterrupted _guiAvailabilityNotifier.junitStarted(); // JUNIT and COMPILER - try { _model.getJUnitModel().junitAll(); } + try { + if(runTestParallel==true) + _model.getJUnitModel().setRunTestParallel(true); + _model.getJUnitModel().junitAll(); + } catch(UnexpectedException e) { _junitInterrupted(e); } catch(Exception e) { _junitInterrupted(new UnexpectedException(e)); } } @@ -6531,7 +6601,7 @@ private void _setUpActions() { _setUpAction(_junitAction, "Test Current", "Run JUnit over the current document"); _setUpAction(_junitAllAction, "Test", "Run JUnit over all open JUnit tests"); - + _setUpAction(_junitAllActionParallel, "Test in Parallel", "Run JUnit over all open JUnit tests in Parallel"); _setUpAction(_coverageAction, "Code Coverage", "Generate code coverage reports"); if (_model.getJavadocModel().isAvailable()) { @@ -6959,6 +7029,8 @@ private JMenu _setUpToolsMenu(int mask, boolean updateKeyboardManager) { _addMenuItem(toolsMenu, _compileAction, KEY_COMPILE, updateKeyboardManager); _addMenuItem(toolsMenu, _junitAllAction, KEY_TEST_ALL, updateKeyboardManager); _addMenuItem(toolsMenu, _junitAction, KEY_TEST, updateKeyboardManager); + _addMenuItem(toolsMenu, _junitAllActionParallel, KEY_TEST_ALL_PARALLEL, updateKeyboardManager); + _addMenuItem(toolsMenu, _junitActionParallel, KEY_TEST_PARALLEL, updateKeyboardManager); toolsMenu.addSeparator(); // Run @@ -7129,6 +7201,7 @@ private JMenu _setUpProjectMenu(int mask, boolean updateKeyboardManager) { // run project _addMenuItem(projectMenu, _compileProjectAction, KEY_COMPILE_PROJECT, updateKeyboardManager); _addMenuItem(projectMenu, _junitProjectAction, KEY_JUNIT_PROJECT, updateKeyboardManager); + _addMenuItem(projectMenu, _junitProjectActionParallel, KEY_JUNIT_PROJECT_PARALLEL, updateKeyboardManager); _addMenuItem(projectMenu, _runProjectAction, KEY_RUN_PROJECT, updateKeyboardManager); _addMenuItem(projectMenu, _cleanAction, KEY_CLEAN_PROJECT, updateKeyboardManager); _addMenuItem(projectMenu, _autoRefreshAction, KEY_AUTO_REFRESH_PROJECT, updateKeyboardManager); @@ -7472,6 +7545,7 @@ private void _setUpToolBar() { _toolBar.add(_runButton = _createToolBarButton(_runAction)); _toolBar.add(_junitButton = _createToolBarButton(_junitAllAction)); + _toolBar.add(_junitButton = _createToolBarButton(_junitAllActionParallel)); _toolBar.add(_createToolBarButton(_javadocAllAction)); _toolBar.add(_coverageButton = _createToolBarButton(_coverageAction)); @@ -7991,6 +8065,7 @@ protected void _popupAction(MouseEvent e) { m.add(_compileProjectAction); m.add(_runProjectAction); m.add(_junitProjectAction); + m.add(_junitProjectActionParallel); m.add(_projectPropertiesAction); } if (folderSelected) { @@ -8007,6 +8082,7 @@ protected void _popupAction(MouseEvent e) { m.add(_closeFolderAction); m.add(_compileFolderAction); m.add(_junitFolderAction); + m.add(_junitPararrelFolderAction); } else if (groupSelectedCount>1) { if (!externalBinSelected && !auxiliaryBinSelected) { @@ -8021,6 +8097,8 @@ else if (groupSelectedCount>1) { createDelegateAction("Compile All Folders ("+groupSelectedCount+")", _compileFolderAction)); m.add(Utilities. createDelegateAction("Test All Folders ("+groupSelectedCount+")", _junitFolderAction)); + m.add(Utilities. + createDelegateAction("Test All Folders in parallel ("+groupSelectedCount+")", _junitPararrelFolderAction)); } } @@ -8038,6 +8116,7 @@ else if (groupSelectedCount>1) { m.add(Utilities.createDelegateAction("Print File Preview...", _printDefDocPreviewAction)); m.add(Utilities.createDelegateAction("Compile File", _compileAction)); m.add(Utilities.createDelegateAction("Test File", _junitAction)); + m.add(Utilities.createDelegateAction("Test File in Parallel", _junitActionParallel)); m.add(Utilities.createDelegateAction("Preview Javadoc for File", _javadocCurrentAction)); m.add(Utilities.createDelegateAction("Run File", _runAction)); m.add(Utilities.createDelegateAction("Run File as Applet", _runAppletAction)); @@ -8080,6 +8159,9 @@ else if (docSelectedCount>1) { m.add(Utilities.createDelegateAction("Close All Files", _closeFolderAction)); m.add(Utilities.createDelegateAction("Compile All Files", _compileFolderAction)); m.add(Utilities.createDelegateAction("Test All Files", _junitFolderAction)); + m.add(Utilities.createDelegateAction("Test All Files in parallel", _junitPararrelFolderAction)); + + } if (externalBinSelected && !auxiliaryBinSelected) { // external bin selected diff --git a/drjava/src/edu/rice/cs/drjava/ui/coverage/CoverageFrame.java b/drjava/src/edu/rice/cs/drjava/ui/coverage/CoverageFrame.java index 4f4744582..aba8abbda 100644 --- a/drjava/src/edu/rice/cs/drjava/ui/coverage/CoverageFrame.java +++ b/drjava/src/edu/rice/cs/drjava/ui/coverage/CoverageFrame.java @@ -305,7 +305,7 @@ private void cancel() { private void startJUnit(){ _model.getJUnitModel().setCoverage(true, this._outputDirSelector.getFileFromField().getPath()); - _mainFrame._junitAll(); + _mainFrame._junitAll(false); CoverageFrame.this.setVisible(false); } diff --git a/drjava/src/edu/rice/cs/util/LogTest.java b/drjava/src/edu/rice/cs/util/LogTest.java index 6c59135db..190a13f1e 100644 --- a/drjava/src/edu/rice/cs/util/LogTest.java +++ b/drjava/src/edu/rice/cs/util/LogTest.java @@ -50,7 +50,6 @@ * @version $Id$ */ public class LogTest extends MultiThreadedTestCase { - public static final int TOL = 2000; // Relying on default constructor @@ -218,7 +217,9 @@ public void testExceptionPrinting() throws IOException { private static final int NUM_THREADS = 50; private static final int DELAY = 100; - // private static final Log ltl = new Log("logtest.txt", false); + + //TODO task + private static final Log ltl = new Log("logtest.txt", true); /** Attempts to test Log's behavior when called concurrently from several sources. Spawns NUM_THREADS LogTestThreads * (see above)that wait a random number between 0 and DELAY milliseconds and then log a message. The function tests @@ -247,9 +248,10 @@ public void testConcurrentWrites() throws IOException, InterruptedException { Date now = new Date(); String s0 = fin.readLine(); Date time0 = log3.parse(s0); - //ltl.log("earlier = " + earlier); - //ltl.log("now = " + now); - //ltl.log("time0 = " + time0); + //TODOtask + ltl.log("earlier = " + earlier); + ltl.log("now = " + now); + ltl.log("time0 = " + time0); assertTrue("Log not opened after 'earlier' and before 'now'", withinTolerance(earlier, time0, now)); String log3OpenMsg = "Log '" + file3.getName() + "' opened: "; @@ -258,6 +260,8 @@ public void testConcurrentWrites() throws IOException, InterruptedException { for (int i = 0; i < NUM_THREADS; i++) { String s1 = fin.readLine(); Date time1 = log3.parse(s1); + //TODOtask + ltl.log("time1 = " + time1); assertTrue("Date of message not after 'earlier' and before 'now'", withinTolerance(earlier, time1, now)); assertTrue("Date of message not after 'previous time' and before 'now'", withinTolerance(time0, time1, now)); assertEquals("Log message", "Test message", getStringAfterDate(s1)); From 83cb9fa38b9199287c736cfd19dc6b0bc94f8969 Mon Sep 17 00:00:00 2001 From: ZhexinXiao Date: Thu, 3 May 2018 01:22:12 -0500 Subject: [PATCH 2/3] Enable build.xml to support test in parallel by add argument -DTestParallel=true and -DThreadCount=4 (This is the number of thread used to test in parallel) modified: build.xml --- drjava/build.xml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drjava/build.xml b/drjava/build.xml index b1c6b0cc8..44bedd2ac 100644 --- a/drjava/build.xml +++ b/drjava/build.xml @@ -61,7 +61,11 @@ - + + + + @@ -348,7 +352,7 @@ - + @@ -443,7 +447,7 @@ - + From 7e49f3e57f65a86e40b0e80b55e16ffcdceba248 Mon Sep 17 00:00:00 2001 From: ZhexinXiao Date: Fri, 4 May 2018 00:39:45 -0500 Subject: [PATCH 3/3] Pull most test case from GlobalModelJUnitTest to GlobalModelJunitTestCase to reuse it. GlobalModelJUnitParallelTest and GlobalModelJUnitTest will extend GlobalModelJunitTestCase and run those testcase in parallel and sequential way. modified: build.xml new file: src/edu/rice/cs/drjava/model/GlobalModelJUnitParallelTest.java modified: src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java new file: src/edu/rice/cs/drjava/model/GlobalModelJunitTestCase.java modified: src/edu/rice/cs/drjava/model/GlobalModelTestCase.java modified: src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java modified: src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java modified: src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java modified: src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java modified: src/edu/rice/cs/util/LogTest.java --- drjava/build.xml | 2 +- .../model/GlobalModelJUnitParallelTest.java | 186 ++++ .../cs/drjava/model/GlobalModelJUnitTest.java | 891 ++++------------- .../model/GlobalModelJunitTestCase.java | 918 ++++++++++++++++++ .../cs/drjava/model/GlobalModelTestCase.java | 14 + .../drjava/model/junit/DefaultJUnitModel.java | 2 +- .../model/junit/JUnitParallelTestManager.java | 2 +- .../cs/drjava/model/junit/MyJUnitCore.java | 6 +- .../model/repl/newjvm/InterpreterJVM.java | 5 +- drjava/src/edu/rice/cs/util/LogTest.java | 4 +- 10 files changed, 1306 insertions(+), 724 deletions(-) create mode 100644 drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitParallelTest.java create mode 100644 drjava/src/edu/rice/cs/drjava/model/GlobalModelJunitTestCase.java diff --git a/drjava/build.xml b/drjava/build.xml index 44bedd2ac..c0ef01e8c 100644 --- a/drjava/build.xml +++ b/drjava/build.xml @@ -512,7 +512,7 @@ - + diff --git a/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitParallelTest.java b/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitParallelTest.java new file mode 100644 index 000000000..6a134f8d5 --- /dev/null +++ b/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitParallelTest.java @@ -0,0 +1,186 @@ +/*BEGIN_COPYRIGHT_BLOCK + * + * Copyright (c) 2001-2017, JavaPLT group at Rice University (drjava@rice.edu). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the + * following conditions are met: + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following + * disclaimer. + * * 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. + * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + * + * This software is Open Source Initiative approved Open Source Software. Open Source Initative Approved is a trademark + * of the Open Source Initiative. + * + * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/ or + * http://sourceforge.net/projects/drjava/ + * + * END_COPYRIGHT_BLOCK*/ + +package edu.rice.cs.drjava.model; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import edu.rice.cs.drjava.model.compiler.CompilerListener; +import edu.rice.cs.drjava.model.junit.*; +import edu.rice.cs.util.Log; +import edu.rice.cs.util.UnexpectedException; +import edu.rice.cs.util.swing.Utilities; + +/** This is the parallel version of GlobalModelJUnitTest, we add _model.getJUnitModel().setRunTestParallel(true); + * before each call to Junit to make sure that test case can be tested in parallel. + * testJUnit4MultiTest_NOJOIN's assertNonTestCaseCount() and listener.assertJUnitStartCount(1) are comment out because multi test are run + * in multiple thread and can't run wait in Global Junit model + * testRealError_NOJOIN is discarded because JunitCore doesn't separate error and failure + * @version $Id$ + */ +public final class GlobalModelJUnitParallelTest extends GlobalModelJunitTestCase { + + private static Log _log = new Log("GlobalModelJUnitParallelTest.txt", true); + + + + /** Tests that a JUnit file with no errors is reported to have no errors. + * @throws Exception if something goes wrong + */ + public void testNoJUnitErrors_NOJOIN() throws Exception { + testNoJUnitErrors_NOJOIN(true); + } + + /** Tests that a JUnit file with an error is reported to have an error. + * @throws Exception if something goes wrong + */ + public void testOneJUnitError_NOJOIN() throws Exception { + testOneJUnitError_NOJOIN(true); + } + + /** Tests that a JUnit file with an error is reported to have an error. + * @throws Exception if something goes wrong + */ + public void testElspethOneJUnitError_NOJOIN() throws Exception { + testElspethOneJUnitError_NOJOIN(true); + } + + + /** Tests that the ui is notified to put up an error dialog if JUnit is run on a non-TestCase. + * @throws Exception if something goes wrong + */ + public void testNonTestCaseError_NOJOIN() throws Exception { + testNonTestCaseError_NOJOIN(true); + } + + /** Tests that the UI is notified to put up an error dialog if JUnit is run on a non-public TestCase. + * @throws Exception if something goes wrong + */ + public void testResultOfNonPublicTestCase_NOJOIN() throws Exception { + testResultOfNonPublicTestCase_NOJOIN(true); + } + + + + /** Tests a document that has no corresponding class file. + * @throws Exception if something goes wrong + */ + public void testNoClassFile() throws Exception { + testNoClassFile(true); + } + + // Commented out because MultiThreadedTestCase objects to the RemoteException thrown by auxiliary unit testing thread + // after resetInteractions kills the slave JVM. + /** Tests that an infinite loop in a test case can be aborted by clicking the Reset button. + * @throws Exception if something goes wrong + */ + public void testInfiniteLoop_NOJOIN() throws Exception { + testInfiniteLoop_NOJOIN(true); + } + + /** Tests that when a JUnit file with no errors, after being saved and compiled, + * has it's contents replaced by a test that should fail, will pass all tests. + * @throws Exception if something goes wrong + */ + public void testUnsavedAndUnCompiledChanges() throws Exception { + testUnsavedAndUnCompiledChanges(true); + } + + /** Verifies that we get a nonTestCase event and that opening a single test file enables testing. + * @throws Exception if something goes wrong + */ + public void safeJUnitAllWithNoValidTests() throws Exception { + safeJUnitAllWithNoValidTests(true); + } + + /** Tests that junit all works with one or two test cases that should pass. + * @throws Exception if something goes wrong + */ + public void safeJUnitAllWithNoErrors() throws Exception { + safeJUnitAllWithNoErrors(true); + } + + /** Tests that junit all works with test cases that do not pass. + * @throws Exception if something goes wrong + */ + public void safeJUnitAllWithErrors() throws Exception { + + safeJUnitAllWithErrors(true); + } + + /** Tests that junit all works with one or two test cases that should pass. + * @throws Exception if something goes wrong + */ + public void safeJUnitStaticInnerClass() throws Exception { + safeJUnitStaticInnerClass(true); + } + + + + /** Tests that when a JUnit file with no errors is compiled and then modified to contain + * an error does not pass unit testing (by running correct class files). + * @throws Exception if something goes wrong + */ + public void testCorrectFilesAfterIncorrectChanges_NOJOIN() throws Exception { + testCorrectFilesAfterIncorrectChanges_NOJOIN(true); + } + + + /** Tests if a JUnit4 style unit test works. + * @throws Exception if something goes wrong + */ + public void testJUnit4StyleTestWorks_NOJOIN() throws Exception { + testJUnit4StyleTestWorks_NOJOIN(true); + } + + /** Tests to see if a JUnit4 style test with multiple test cases passes. + * @throws Exception if something goes wrong + */ + public void testJUnit4MultiTest_NOJOIN() throws Exception { + testJUnit4MultiTest_NOJOIN(true); + } + + + /** Tests to see if a JUnit4 style test with no test cases will not run. + * @throws Exception if something goes wrong + */ + public void testJUnit4NoTest_NOJOIN() throws Exception { + testJUnit4NoTest_NOJOIN(true); + } + + /** Tests to see if a JUnit4 style test with a test method and multiple nonTest methods will run. + * @throws Exception if something goes wrong + */ + public void testJUnit4TwoMethod1Test_NOJOIN() throws Exception { + testJUnit4TwoMethod1Test_NOJOIN(true); + } +} diff --git a/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java b/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java index f6c327453..27df61310 100644 --- a/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java +++ b/drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java @@ -34,6 +34,8 @@ import java.util.Arrays; import java.util.List; +import edu.rice.cs.drjava.model.GlobalModelTestCase.FileSelector; +import edu.rice.cs.drjava.model.GlobalModelTestCase.JUnitTestListener; import edu.rice.cs.drjava.model.compiler.CompilerListener; import edu.rice.cs.drjava.model.junit.*; import edu.rice.cs.util.Log; @@ -43,9 +45,9 @@ /** A test of Junit testing support in the GlobalModel. * @version $Id$ */ -public final class GlobalModelJUnitTest extends GlobalModelTestCase { +public final class GlobalModelJUnitTest extends GlobalModelJunitTestCase { - private static Log _log = new Log("GlobalModel.txt", false); + private static Log _log = new Log("GlobalModelJUnitTest.txt", false); /** Whether or not to print debugging output. */ static final boolean printMessages = true; @@ -179,89 +181,26 @@ public final class GlobalModelJUnitTest extends GlobalModelTestCase { " public void testAB() { assertTrue(\"this is true\", true); } " + " }"; - /** Tests that a JUnit file with no errors is reported to have no errors. - * @throws Exception if something goes wrong - */ - public void testNoJUnitErrors_NOJOIN() throws Exception { - _log.log("----testNoJUnitErrors-----"); -// Utilities.show("Running testNoJUnitErrors"); - - final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); - final File file = new File(_tempDir, "MonkeyTestPass.java"); - saveFile(doc, new FileSelector(file)); - JUnitTestListener listener = new JUnitTestListener(); - _model.addListener(listener); - - listener.compile(doc); // synchronously compiles doc - listener.checkCompileOccurred(); - - listener.runJUnit(doc); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - listener.assertJUnitStartCount(1); - - _log.log("errors: " + _model.getJUnitModel().getJUnitErrorModel()); - - listener.assertNonTestCaseCount(0); - assertEquals("test case should have no errors reported", 0, - _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); - - _model.removeListener(listener); - _log.log("testNoJUnitErrors completed"); - } - - /** Tests that a JUnit file with an error is reported to have an error. * @throws Exception if something goes wrong */ - public void testOneJUnitError_NOJOIN() throws Exception { - _log.log("----testOneJUnitError-----"); -// Utilities.show("Running testOneJUnitError"); - - final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_FAIL_TEXT); - final File file = new File(_tempDir, "MonkeyTestFail.java"); - saveFile(doc, new FileSelector(file)); - JUnitTestListener listener = new JUnitTestListener(); - _model.addListener(listener); - - listener.compile(doc); - listener.checkCompileOccurred(); - - listener.runJUnit(_model.getJUnitModel()); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - assertEquals("test case has one error reported", 1, _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); - _model.removeListener(listener); - - _log.log("testOneJUnitError completed"); - } - - /** Tests that a JUnit file with an error is reported to have an error. - * @throws Exception if something goes wrong - */ - public void testElspethOneJUnitError_NOJOIN() throws Exception { - _log.log("----testElspethOneJUnitError-----"); -// Utilities.show("Running testElspethOneJunitError"); - - OpenDefinitionsDocument doc = setupDocument(ELSPETH_ERROR_TEXT); - final File file = new File(_tempDir, "Elspeth.java"); - saveFile(doc, new FileSelector(file)); - JUnitTestListener listener = new JUnitTestListener(); - _model.addListener(listener); - - listener.compile(doc); - listener.checkCompileOccurred(); - - listener.runJUnit(doc); - - JUnitErrorModel junitErrorModel = _model.getJUnitModel().getJUnitErrorModel(); - assertEquals("test case has one error reported", 1, junitErrorModel.getNumErrors()); - assertTrue("first error should be an error not a warning", !junitErrorModel.getError(0).isWarning()); - _model.removeListener(listener); - - _log.log("testElspethOneJUnitError completed"); - } - + public void testNoJUnitErrors_NOJOIN() throws Exception { + testNoJUnitErrors_NOJOIN(true); + } + + /** Tests that a JUnit file with an error is reported to have an error. + * @throws Exception if something goes wrong + */ + public void testOneJUnitError_NOJOIN() throws Exception { + testOneJUnitError_NOJOIN(true); + } + + /** Tests that a JUnit file with an error is reported to have an error. + * @throws Exception if something goes wrong + */ + public void testElspethOneJUnitError_NOJOIN() throws Exception { + testElspethOneJUnitError_NOJOIN(true); + } /** Tests that a test class which throws a *real* Error (not an Exception) is handled correctly. * @throws Exception if something goes wrong */ @@ -277,7 +216,7 @@ public void testRealError_NOJOIN() throws Exception { listener.compile(doc); listener.checkCompileOccurred(); - + _model.getJUnitModel().setRunTestParallel(false); listener.runJUnit(doc); // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify @@ -292,648 +231,168 @@ public void testRealError_NOJOIN() throws Exception { * @throws Exception if something goes wrong */ public void testNonTestCaseError_NOJOIN() throws Exception { - _log.log("+++Starting testNonTestCaseError"); -// Utilities.show("Running testNonTestCaseError"); - - final OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT); - final File file = new File(_tempDir, "NonTestCase.java"); - saveFile(doc, new FileSelector(file)); - - JUnitTestListener listener = new JUnitNonTestListener(); - - _model.addListener(listener); - - listener.compile(doc); - listener.checkCompileOccurred(); - - listener.runJUnit(doc); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - _log.log("after test"); - - // Check events fired - listener.assertJUnitStartCount(0); // JUnit is never started - listener.assertJUnitEndCount(0); // JUnit never started and hence never ended - listener.assertNonTestCaseCount(1); - listener.assertJUnitSuiteStartedCount(0); - listener.assertJUnitTestStartedCount(0); - listener.assertJUnitTestEndedCount(0); - _model.removeListener(listener); - - _log.log("testNonTestCaseError completed"); + testNonTestCaseError_NOJOIN(true); } /** Tests that the UI is notified to put up an error dialog if JUnit is run on a non-public TestCase. * @throws Exception if something goes wrong */ public void testResultOfNonPublicTestCase_NOJOIN() throws Exception { - _log.log("----testResultOfNonPublicTestCase-----"); -// Utilities.show("Running testResultOfNonPublicTestCase"); - - final OpenDefinitionsDocument doc = setupDocument(NONPUBLIC_TEXT); - final File file = new File(_tempDir, "NonPublic.java"); - saveFile(doc, new FileSelector(file)); - - JUnitTestListener listener = new JUnitTestListener(); - - _model.addListener(listener); - - listener.compile(doc); - listener.checkCompileOccurred(); - - listener.runJUnit(doc); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - _log.log("After test"); - - // Check events fired - listener.assertJUnitStartCount(1); - listener.assertJUnitEndCount(1); - - assertEquals("test case has one error reported", 1, _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); - _model.removeListener(listener); - - _log.log("testResultOfNonPublicTestCase completed"); - } - - /* This test has become inconsistent with DrJava behavior. If a document's file no longer exists and no class file - * exists, DrJava will detect that there is no valid class file for the document and ask the user to compile the - * file - */ -// public void testDoNotRunJUnitIfFileHasBeenMoved() throws Exception { -// if (printMessages) System.err.println("----testDoNotRunJUnitIfFileHasBeenMoved-----"); -//// Utilities.show("Running testDoNotRunJUnitIfFileHasBeenMoved"); -// -// final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); -// final File file = new File(_tempDir, "MonkeyTestPass.java"); -// doc.saveFile(new FileSelector(file)); -// -// JUnitTestListener listener = new JUnitTestListener(); -// -// _model.addListener(listener); -// file.delete(); -// -// listener.runJUnit(doc); -// -// listener.assertJUnitStartCount(0); -// listener.assertJUnitTestStartedCount(0); -// -// _model.removeListener(listener); -// _log.log("testDoNotRunJUnitIfFileHasBeenMoved completed"); -// } - - /** Tests a document that has no corresponding class file. - * @throws Exception if something goes wrong - */ - public void testNoClassFile() throws Exception { - _log.log("----testNoClassFile-----"); -// Utilities.show("Running testNoClassFile"); - - final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); - final File file = new File(_tempDir, "MonkeyTestPass.java"); - saveFile(doc, new FileSelector(file)); - - JUnitTestListener listener = new JUnitCompileBeforeTestListener(); - - _model.addListener(listener); - -// Utilities.show("calling _runJunit in testNoClassFile"); - - listener.runJUnit(doc); -// Utilities.showDebug("Junit run completed"); - - _log.log("after test"); - listener.assertCompileBeforeJUnitCount(1); - listener.assertNonTestCaseCount(0); - listener.assertJUnitStartCount(1); - listener.assertJUnitEndCount(1); - listener.assertJUnitSuiteStartedCount(1); - listener.assertJUnitTestStartedCount(1); - listener.assertJUnitTestEndedCount(1); - _model.removeListener(listener); - _log.log("testNoClassFile completed"); - } - - // Commented out because MultiThreadedTestCase objects to the RemoteException thrown by auxiliary unit testing thread - // after resetInteractions kills the slave JVM. - /** Tests that an infinite loop in a test case can be aborted by clicking the Reset button. - * @throws Exception if something goes wrong - */ - public void testInfiniteLoop_NOJOIN() throws Exception { - _log.log("----testInfiniteLoop-----"); -// Utilities.show("Running testInfiniteLoop"); - - final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_INFINITE_TEXT); - final File file = new File(_tempDir, "MonkeyTestInfinite.java"); - saveFile(doc, new FileSelector(file)); - - JUnitTestListener listener = new JUnitTestListener(false) { - public void junitSuiteStarted(int numTests) { - assertEquals("should run 1 test", 1, numTests); - synchronized(this) { junitSuiteStartedCount++; } - // kill the infinite test once testSuiteProcessing starts - _model.resetInteractions(new File(System.getProperty("user.dir"))); - } - }; - - _model.addListener(listener); - listener.compile(doc); - - _log.log("Compilation of infinite loop completed"); - - if (_model.getCompilerModel().getNumErrors() > 0) { - fail("compile failed: " + getCompilerErrorString()); - } - listener.checkCompileOccurred(); - - _log.log("CheckCompile completed"); -// _model.removeListener(listener); -// -// _model.addListener(listener2); - - listener.logJUnitStart(); - try { - _log.log("Starting JUnit"); - doc.startJUnit(); - listener.waitJUnitDone(); - // this waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - // auxiliary thread silently swallows the exception and terminates. - } - catch (Exception e) { fail("Aborting unit testing runs recovery code in testing thread; no exception is thrown"); } - - listener.waitResetDone(); // reset should occur when test suite is started - - _log.log("ResetDone"); - - _log.log("after test"); - listener.assertJUnitStartCount(1); - _model.removeListener(listener); - listener.assertJUnitEndCount(1); // Testing was aborted after junitStarted(); junitEnded called in recovery code - _log.log("Reached Test End"); - _log.log("testInfiniteLoop completed"); + testResultOfNonPublicTestCase_NOJOIN(true); } - /** Tests that when a JUnit file with no errors, after being saved and compiled, - * has it's contents replaced by a test that should fail, will pass all tests. - * @throws Exception if something goes wrong - */ - public void testUnsavedAndUnCompiledChanges() throws Exception { - _log.log("-----testUnsavedAndUnCompiledChanges-----"); - - OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); - final File file = new File(_tempDir, "MonkeyTestPass.java"); - saveFile(doc, new FileSelector(file)); - - List docs = _model.getSortedOpenDefinitionsDocuments(); - - final OpenDefinitionsDocument untitled = docs.get(0); - - _log.log("Untitled file is named: " + untitled.getName()); - - Utilities.invokeAndWait(new Runnable() { - public void run() { - untitled.quitFile(); - _model.closeFileWithoutPrompt(untitled); - } - }); - - // set up test listener for compile command; automatically checks that compilation is performed - JUnitTestListener listener = new JUnitCompileBeforeTestListener(); - _model.addListener(listener); - - testStartCompile(doc); - - _log.log("Ordinary compile completed"); - listener.waitCompileDone(); - - listener.resetCompileCounts(); - - changeDocumentText(MONKEYTEST_PASS_ALT_TEXT, doc); - _log.log("document changed; modifiedSinceSave = " + doc.isModifiedSinceSave()); - - listener.runJUnit(doc); - _log.log("JUnit completed"); - - /* Unsaved document forces both saveBeforeCompile and compileBeforeTest */ - - listener.assertSaveBeforeCompileCount(1); - listener.assertCompileBeforeJUnitCount(1); - listener.assertNonTestCaseCount(0); - listener.assertJUnitStartCount(1); - listener.assertJUnitEndCount(1); - listener.assertJUnitSuiteStartedCount(1); - listener.assertJUnitTestStartedCount(1); - listener.assertJUnitTestEndedCount(1); - - _log.log("after test"); - _model.removeListener(listener); - - assertEquals("test case should have no errors reported after modifying", 0, - _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); - - saveFile(doc, new FileSelector(file)); - - listener = new JUnitTestListener(); - _model.addListener(listener); - - - assertEquals("test case should have no errors reported after saving", 0, - _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); - _model.removeListener(listener); - - _log.log("testUnsavedAndUnCompiledChanges completed"); - } - - /** Verifies that we get a nonTestCase event and that opening a single test file enables testing. - * @throws Exception if something goes wrong - */ - public void safeJUnitAllWithNoValidTests() throws Exception { - - _log.log("-----testJUnitAllWithNoValidTests-----"); - - JUnitNonTestListener listener = new JUnitNonTestListener(true); - _model.addListener(listener); - - listener.runJUnit(_model.getJUnitModel()); - - listener.assertNonTestCaseCount(1); - listener.assertJUnitSuiteStartedCount(0); - listener.assertJUnitTestStartedCount(0); - listener.assertJUnitTestEndedCount(0); - _model.removeListener(listener); - - JUnitCompileBeforeTestListener listener2 = new JUnitCompileBeforeTestListener(); - _model.addListener(listener2); - OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT); - File file = new File(_tempDir, "NonTestCase.java"); - _log.log("-----> file = " + file + " -- canWrite() = " + file.canWrite() + " -- exists() = " + file.exists()); - saveFile(doc, new FileSelector(file)); - - listener2.compile(doc); - listener2.checkCompileOccurred(); - - listener2.resetCompileCounts(); - - // Opending Test - File file2 = new File(_tempDir, "MonkeyTestPass.java"); - OpenDefinitionsDocument doc2 = setupDocument(MONKEYTEST_PASS_TEXT); - saveFile(doc2, new FileSelector(file2)); - listener2.runJUnit(_model.getJUnitModel()); - - listener2.assertNonTestCaseCount(0); - listener2.assertJUnitSuiteStartedCount(1); - listener2.assertJUnitTestStartedCount(1); - listener2.assertJUnitTestEndedCount(1); - _model.removeListener(listener2); - - _log.log("testJUnitAllWithNoValidTests completed"); - } - - /** Tests that junit all works with one or two test cases that should pass. - * @throws Exception if something goes wrong - */ - public void safeJUnitAllWithNoErrors() throws Exception { -// _log.log("Starting testJUnitAllWithNoErrors"); - -// final OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT); -// final File file = new File(_tempDir, "NonTestCase.java"); -// saveFile(doc, new FileSelector(file)); -// -// JUnitTestListener listener = new JUnitNonTestListener(true); -// -// _model.addListener(listener); -// -// listener.compile(doc); -// listener.checkCompileOccurred(); -// -// _log.log("Compiled first doc"); -// - OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); - File file = new File(_tempDir, "MonkeyTestPass.java"); - saveFile(doc, new FileSelector(file)); - JUnitTestListener listener = new JUnitNonTestListener(true); - _model.addListener(listener); - listener.compile(doc); - listener.checkCompileOccurred(); - - listener.runJUnit(_model.getJUnitModel()); - - listener.assertNonTestCaseCount(0); - listener.assertJUnitSuiteStartedCount(1); - listener.assertJUnitTestStartedCount(1); - listener.assertJUnitTestEndedCount(1); - _model.removeListener(listener); - - doc = setupDocument(HAS_MULTIPLE_TESTS_PASS_TEXT); - file = new File(_tempDir, "HasMultipleTestsPass.java"); - saveFile(doc, new FileSelector(file)); - - listener = new JUnitNonTestListener(true); - _model.addListener(listener); - - listener.compile(doc); - - listener.runJUnit(_model.getJUnitModel()); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - listener.assertNonTestCaseCount(0); - listener.assertJUnitSuiteStartedCount(1); - listener.assertJUnitTestStartedCount(3); - listener.assertJUnitTestEndedCount(3); - _model.removeListener(listener); - - _log.log("testJUnitAllWithNoErrors completed"); - } - - /** Tests that junit all works with test cases that do not pass. - * @throws Exception if something goes wrong - */ - public void safeJUnitAllWithErrors() throws Exception { - - _log.log("-----testJUnitAllWithErrors-----"); - - OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_ERROR_TEXT); - OpenDefinitionsDocument doc2 = setupDocument(MONKEYTEST_FAIL_TEXT); - File file = new File(_tempDir, "MonkeyTestError.java"); - File file2 = new File(_tempDir, "MonkeyTestFail.java"); - saveFile(doc, new FileSelector(file)); - saveFile(doc2, new FileSelector(file2)); - JUnitNonTestListener listener = new JUnitNonTestListener(true); - _model.addListener(listener); - listener.compile(doc); - listener.checkCompileOccurred(); - listener.resetCompileCounts(); - listener.compile(doc2); - listener.checkCompileOccurred(); - - listener.runJUnit(_model.getJUnitModel()); - - listener.assertNonTestCaseCount(0); - listener.assertJUnitSuiteStartedCount(1); - listener.assertJUnitTestStartedCount(2); - listener.assertJUnitTestEndedCount(2); - _model.removeListener(listener); - - JUnitErrorModel junitErrorModel = _model.getJUnitModel().getJUnitErrorModel(); - assertEquals("test case has one error reported", 2, junitErrorModel.getNumErrors()); - - assertTrue("first error should be an error", junitErrorModel.getError(0).isWarning()); - assertFalse("second error should be a failure", junitErrorModel.getError(1).isWarning()); - - _log.log("testJUnitAllWithErrors completed"); - } - - /** Tests that junit all works with one or two test cases that should pass. - * @throws Exception if something goes wrong - */ - public void safeJUnitStaticInnerClass() throws Exception { - _log.log("-----testJUnitAllWithStaticInnerClass-----"); - - OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT); - OpenDefinitionsDocument doc2 = setupDocument(STATIC_INNER_TEST_TEXT); - File file = new File(_tempDir, "NonTestCase.java"); - File file2 = new File(_tempDir, "StaticInnerTestCase.java"); - saveFile(doc, new FileSelector(file)); - saveFile(doc2, new FileSelector(file2)); - - JUnitNonTestListener listener = new JUnitNonTestListener(true); - _model.addListener(listener); - listener.compile(doc); - listener.checkCompileOccurred(); - listener.resetCompileCounts(); - listener.compile(doc2); - listener.checkCompileOccurred(); - - listener.runJUnit(_model.getJUnitModel()); - - listener.assertNonTestCaseCount(0); - listener.assertJUnitSuiteStartedCount(1); - listener.assertJUnitTestStartedCount(2); - listener.assertJUnitTestEndedCount(2); - _model.removeListener(listener); - _log.log("----testJUnitAllWithNoErrors-----"); - - _log.log("testJUnitStaticInnerClass completed"); - } - - /** Tests that testing an uncompiled but correct group of files will first compile and then run test. */ - public class JUnitCompileBeforeTestListener extends JUnitTestListener { - - /* Method copied by _mainListener in MainFrame. */ - public void compileBeforeJUnit(final CompilerListener testAfterCompile, List outOfSync) { - _log.log("compileBeforeJUnit called in listener " + this); - synchronized(this) { compileBeforeJUnitCount++; } - // Compile all open source files - _model.getCompilerModel().addListener(testAfterCompile); // listener removes itself - _log.log("Calling _compileAll()"); - try { _model.getCompilerModel().compileAll(); /* instead of invoking MainFrame._compileAll() */ } - catch(IOException e) { fail("Compile step generated IOException"); } - - _log.log("Compilation finished"); - } - - public void saveBeforeCompile() { - _log.log("saveBeforeCompile called in " + this); - synchronized(this) { saveBeforeCompileCount++; } - /** Assumes that DrJava is in flat file mode! */ - saveAllFiles(_model, new FileSaveSelector() { - public File getFile() { throw new UnexpectedException ("Test should not ask for save file name"); } - public boolean warnFileOpen(File f) { return false; } - public boolean verifyOverwrite(File f) { return true; } - public boolean shouldSaveAfterFileMoved(OpenDefinitionsDocument doc, File oldFile) { return false; } - public boolean shouldUpdateDocumentState() { return true; } - }); - } - public void fileSaved(OpenDefinitionsDocument doc) { } - } - - /** Tests that when a JUnit file with no errors is compiled and then modified to contain - * an error does not pass unit testing (by running correct class files). - * @throws Exception if something goes wrong - */ - public void testCorrectFilesAfterIncorrectChanges_NOJOIN() throws Exception { - _log.log("----testCorrectFilesAfterIncorrectChanges-----"); - -// OpenDefinitionsDocument doc0 = setupDocument(NON_TESTCASE_TEXT); -// JUnitNonTestListener listener0 = new JUnitNonTestListener(true); -// File file = new File(_tempDir, "NonTestCase.java"); -// saveFile(doc0, new FileSelector(file)); -// _model.addListener(listener0); -// -// listener0.compile(doc0); -// listener0.checkCompileOccurred(); -// _model.removeListener(listener0); -//// What is the preceding code segment supposed to test; it has already been done! - - final OpenDefinitionsDocument doc1 = setupDocument(MULTI_CLASSES_IN_FILE_TEXT); - final File file = new File(_tempDir, "DJTest.java"); - saveFile(doc1, new FileSelector(file)); - _log.log("In testCorrectFilesAfterIncorrectChanges, DJTest.java = \n" + doc1.getText()); - - final JUnitNonTestListener listener1 = new JUnitNonTestListener(true); - _model.addListener(listener1); - listener1.compile(doc1); - listener1.checkCompileOccurred(); - assertCompileErrorsPresent(false); - listener1.runJUnit(_model.getJUnitModel()); - listener1.assertJUnitSuiteStartedCount(1); - listener1.assertJUnitTestStartedCount(1); - listener1.assertJUnitTestEndedCount(1); - listener1.assertNonTestCaseCount(0); - _model.removeListener(listener1); - doc1.remove(87,4); - - JUnitTestListener listener2 = new JUnitCompileBeforeTestListener(); - _model.addListener(listener2); - listener2.runJUnit(doc1); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - _log.log("after test"); - listener2.assertCompileBeforeJUnitCount(1); - listener2.assertNonTestCaseCount(1); - listener2.assertJUnitStartCount(0); - listener2.assertJUnitEndCount(0); - listener2.assertJUnitSuiteStartedCount(0); - listener2.assertJUnitTestStartedCount(0); - listener2.assertJUnitTestEndedCount(0); - _model.removeListener(listener2); - _log.log("testCorrectFilesAfterIncorrectChanges completed"); - } - - /** Tests if a JUnit4 style unit test works. - * @throws Exception if something goes wrong - */ - public void testJUnit4StyleTestWorks_NOJOIN() throws Exception { - - _log.log("----testJUnit4StyleTestWorks-----"); - - File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4StyleTest.java"); - final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); - - final File file = new File(_tempDir, "JUnit4StyleTest.java"); - saveFile(doc, new FileSelector(file)); - JUnitTestListener listener = new JUnitTestListener(); - _model.addListener(listener); - - listener.compile(doc); // synchronously compiles doc - listener.checkCompileOccurred(); - - listener.runJUnit(doc); - _log.log("errors: " + Arrays.toString(_model.getJUnitModel().getJUnitErrorModel().getErrors())); - - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - listener.assertJUnitStartCount(1); - + /** + * Tests a document that has no corresponding class file. + * + * @throws Exception + * if something goes wrong + */ + public void testNoClassFile() throws Exception { + testNoClassFile(true); + } - listener.assertNonTestCaseCount(0); - assertEquals("test case should have no errors reported", 0, - _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); - - _model.removeListener(listener); - _log.log("----testJUnit4StyleTestWorks completed"); - } - - /** Tests to see if a JUnit4 style test with multiple test cases passes. - * @throws Exception if something goes wrong - */ - public void testJUnit4MultiTest_NOJOIN() throws Exception { - - _log.log("----testJUnit4MultiTest-----"); - - File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4MultiTest.java"); - final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); - - final File file = new File(_tempDir, "JUnit4MultiTest.java"); - saveFile(doc, new FileSelector(file)); - JUnitTestListener listener = new JUnitTestListener(); - _model.addListener(listener); - - listener.compile(doc); // synchronously compiles doc - listener.checkCompileOccurred(); - - listener.runJUnit(doc); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - _log.log("errors: " + Arrays.toString(_model.getJUnitModel().getJUnitErrorModel().getErrors())); - - listener.assertJUnitStartCount(1); - - listener.assertNonTestCaseCount(0); - assertEquals("test case should have no errors reported", 0, - _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); - - _model.removeListener(listener); - _log.log("testJUnit4SMultiTest completed"); - } - - - /** Tests to see if a JUnit4 style test with no test cases will not run. - * @throws Exception if something goes wrong - */ - public void testJUnit4NoTest_NOJOIN() throws Exception { - _log.log("----testJUnit4NoTest-----"); - - File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4NoTest.java"); - final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); - final File file = new File(_tempDir, "JUnit4NoTest.java"); - saveFile(doc, new FileSelector(file)); - - JUnitTestListener listener = new JUnitNonTestListener(); - - _model.addListener(listener); - - listener.compile(doc); - listener.checkCompileOccurred(); - - listener.runJUnit(doc); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - _log.log("after test"); - - // Check events fired - listener.assertJUnitStartCount(0); // JUnit is never started - listener.assertJUnitEndCount(0); // JUnit never started and hence never ended - listener.assertNonTestCaseCount(1); - listener.assertJUnitSuiteStartedCount(0); - listener.assertJUnitTestStartedCount(0); - listener.assertJUnitTestEndedCount(0); - _model.removeListener(listener); - - _log.log("testJUnit4NoTest completed"); - } - - /** Tests to see if a JUnit4 style test with a test method and multiple nonTest methods will run. - * @throws Exception if something goes wrong - */ - public void testJUnit4TwoMethod1Test_NOJOIN() throws Exception { - - _log.log("----testJUnit4TwoMethod1Test-----"); - - File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4TwoMethod1Test.java"); - final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); - - final File file = new File(_tempDir, "JUnit4TwoMethod1Test.java"); - saveFile(doc, new FileSelector(file)); - JUnitTestListener listener = new JUnitTestListener(); - _model.addListener(listener); - - listener.compile(doc); // synchronously compiles doc - listener.checkCompileOccurred(); - - listener.runJUnit(doc); - // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify - - _log.log("errors: " + Arrays.toString(_model.getJUnitModel().getJUnitErrorModel().getErrors())); - - listener.assertJUnitStartCount(1); - listener.assertNonTestCaseCount(0); - assertEquals("test case should have no errors reported", 0, - _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); - - _model.removeListener(listener); - _log.log("testJUnit4TwoMethod1Test completed"); - } + // Commented out because MultiThreadedTestCase objects to the RemoteException + // thrown by auxiliary unit testing thread + // after resetInteractions kills the slave JVM. + /** + * Tests that an infinite loop in a test case can be aborted by clicking the + * Reset button. + * + * @throws Exception + * if something goes wrong + */ + public void testInfiniteLoop_NOJOIN() throws Exception { + testInfiniteLoop_NOJOIN(true); + } + + /** + * Tests that when a JUnit file with no errors, after being saved and compiled, + * has it's contents replaced by a test that should fail, will pass all tests. + * + * @throws Exception + * if something goes wrong + */ + public void testUnsavedAndUnCompiledChanges() throws Exception { + testUnsavedAndUnCompiledChanges(true); + } + + /** + * Verifies that we get a nonTestCase event and that opening a single test file + * enables testing. + * + * @throws Exception + * if something goes wrong + */ + public void safeJUnitAllWithNoValidTests() throws Exception { + safeJUnitAllWithNoValidTests(true); + } + + /** + * Tests that junit all works with one or two test cases that should pass. + * + * @throws Exception + * if something goes wrong + */ + public void safeJUnitAllWithNoErrors() throws Exception { + safeJUnitAllWithNoErrors(true); + } + + /** + * Tests that junit all works with test cases that do not pass. + * + * @throws Exception + * if something goes wrong + */ + public void safeJUnitAllWithErrors() throws Exception { + + safeJUnitAllWithErrors(true); + } + + /** + * Tests that junit all works with one or two test cases that should pass. + * + * @throws Exception + * if something goes wrong + */ + public void safeJUnitStaticInnerClass() throws Exception { + safeJUnitStaticInnerClass(true); + } + + /** + * Tests that when a JUnit file with no errors is compiled and then modified to + * contain an error does not pass unit testing (by running correct class files). + * + * @throws Exception + * if something goes wrong + */ + public void testCorrectFilesAfterIncorrectChanges_NOJOIN() throws Exception { + testCorrectFilesAfterIncorrectChanges_NOJOIN(true); + } + + /** + * Tests if a JUnit4 style unit test works. + * + * @throws Exception + * if something goes wrong + */ + public void testJUnit4StyleTestWorks_NOJOIN() throws Exception { + testJUnit4StyleTestWorks_NOJOIN(true); + } + + + /** Tests to see if a JUnit4 style test with multiple test cases passes. + * @throws Exception if something goes wrong + */ + public void testJUnit4MultiTest_NOJOIN() throws Exception { + + _log.log("----testJUnit4MultiTest-----"); + + File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4MultiTest.java"); + final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); + + final File file = new File(_tempDir, "JUnit4MultiTest.java"); + saveFile(doc, new FileSelector(file)); + JUnitTestListener listener = new JUnitTestListener(); + _model.addListener(listener); + + listener.compile(doc); // synchronously compiles doc + listener.checkCompileOccurred(); + + listener.runJUnit(doc); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + _log.log("errors: " + Arrays.toString(_model.getJUnitModel().getJUnitErrorModel().getErrors())); + + listener.assertJUnitStartCount(1); + + listener.assertNonTestCaseCount(0); + assertEquals("test case should have no errors reported", 0, + _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + + _model.removeListener(listener); + _log.log("testJUnit4SMultiTest completed"); + } + + /** + * Tests to see if a JUnit4 style test with no test cases will not run. + * + * @throws Exception + * if something goes wrong + */ + public void testJUnit4NoTest_NOJOIN() throws Exception { + testJUnit4NoTest_NOJOIN(true); + } + + /** + * Tests to see if a JUnit4 style test with a test method and multiple nonTest + * methods will run. + * + * @throws Exception + * if something goes wrong + */ + public void testJUnit4TwoMethod1Test_NOJOIN() throws Exception { + testJUnit4TwoMethod1Test_NOJOIN(true); + } } diff --git a/drjava/src/edu/rice/cs/drjava/model/GlobalModelJunitTestCase.java b/drjava/src/edu/rice/cs/drjava/model/GlobalModelJunitTestCase.java new file mode 100644 index 000000000..9b78eafdb --- /dev/null +++ b/drjava/src/edu/rice/cs/drjava/model/GlobalModelJunitTestCase.java @@ -0,0 +1,918 @@ + +/*BEGIN_COPYRIGHT_BLOCK +* +* Copyright (c) 2001-2017, JavaPLT group at Rice University (drjava@rice.edu). All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +* following conditions are met: +* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following +* disclaimer. +* * 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. +* * Neither the names of DrJava, the JavaPLT group, Rice University, nor the names of its contributors may be used +* to endorse or promote products derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. +* +* This software is Open Source Initiative approved Open Source Software. Open Source Initative Approved is a trademark +* of the Open Source Initiative. +* +* This file is part of DrJava. Download the current version of this project from http://www.drjava.org/ or +* http://sourceforge.net/projects/drjava/ +* +* END_COPYRIGHT_BLOCK*/ + +package edu.rice.cs.drjava.model; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import edu.rice.cs.drjava.model.compiler.CompilerListener; +import edu.rice.cs.drjava.model.junit.*; +import edu.rice.cs.util.Log; +import edu.rice.cs.util.UnexpectedException; +import edu.rice.cs.util.swing.Utilities; + +/** To reuse the code, we add this class to contain the method that both GlobalModelJunitTest and GlobalModelParallelJunitTest will use + * @version $Id$ + */ +public abstract class GlobalModelJunitTestCase extends GlobalModelTestCase { + + private static Log _log = new Log("GlobalModelJunitTestCase.txt", true); + + /** Whether or not to print debugging output. */ + static final boolean printMessages = true; + + private static final String ELSPETH_ERROR_TEXT = + "import junit.framework.TestCase;" + + "public class Elspeth extends TestCase {" + + " public void testMe() {" + + " String s = \"elspeth\";" + + " assertEquals(\"they match\", s, \"elspeth4\");" + + " }" + + " public Elspeth() {" + + " super();" + + " }" + + " public java.lang.String toString() {" + + " return \"Elspeth(\" + \")\";" + + " }" + + " public boolean equals(java.lang.Object o) {" + + " if ((o == null) || getClass() != o.getClass()) return false;" + + " return true;" + + " }" + + " public int hashCode() {" + + " return getClass().hashCode();" + + " }" + + "}"; + + private static final String MONKEYTEST_PASS_TEXT = + "import junit.framework.*; \n" + + "import java.io.*; \n" + + "public class MonkeyTestPass extends TestCase { \n" + + " public MonkeyTestPass(String name) { super(name); } \n" + + " public void testShouldPass() { \n" + + " assertEquals(\"monkey\", \"monkey\"); \n" + + " } \n" + + "}\n"; + + private static final String MONKEYTEST_PASS_ALT_TEXT = + "import junit.framework.*; \n" + + "import java.io.*; \n" + + "public class MonkeyTestPass extends TestCase { \n" + + " public MonkeyTestPass(String name) { super(name); } \n" + + " public void testShouldPass() { \n" + + " assertEquals(\"monkeys\", \"monkeys\"); \n" + + " } \n" + + "}\n"; + + private static final String MONKEYTEST_FAIL_TEXT = + "import junit.framework.*; " + + "public class MonkeyTestFail extends TestCase { " + + " public MonkeyTestFail(String name) { super(name); } " + + " public void testShouldFail() { " + + " assertEquals(\"monkey\", \"baboon\"); " + + " } " + + "}"; + + private static final String MONKEYTEST_ERROR_TEXT = + "import junit.framework.*; " + + "public class MonkeyTestError extends TestCase { " + + " public MonkeyTestError(String name) { super(name); } " + + " public void testThrowsError() { " + + " throw new Error(\"This is an error.\"); " + + " } " + + "}"; + +// private static final String MONKEYTEST_COMPILEERROR_TEXT = +// "import junit.framework.*; " + +// "public class MonkeyTestCompileError extends TestCase { " + +// " Object MonkeyTestFail(String name) { super(name); } " + +// " public void testShouldFail() { " + +// " assertEquals(\"monkey\", \"baboon\"); " + +// " } " + +// "}"; + + private static final String NONPUBLIC_TEXT = + "import junit.framework.*; " + + "class NonPublic extends TestCase { " + + " NonPublic(String name) { super(name); } " + + " void testShouldFail() { " + + " assertEquals(\"monkey\", \"baboon\"); " + + " } " + + "}"; + + private static final String NON_TESTCASE_TEXT = + "public class NonTestCase {}"; + + private static final String MONKEYTEST_INFINITE_TEXT = + "import junit.framework.*; " + + "public class MonkeyTestInfinite extends TestCase { " + + " public MonkeyTestInfinite(String name) { super(name); } " + + " public void testInfinite() { " + + " while(true) {}" + + " } " + + "}"; + + private static final String HAS_MULTIPLE_TESTS_PASS_TEXT = + "import junit.framework.*; " + + "public class HasMultipleTestsPass extends TestCase { " + + " public HasMultipleTestsPass(String name) { super(name); } " + + " public void testShouldPass() { " + + " assertEquals(\"monkey\", \"monkey\"); " + + " } " + + " public void testShouldAlsoPass() { " + + " assertTrue(true); " + + " } " + + "}"; + + private static final String STATIC_INNER_TEST_TEXT = + "import junit.framework.TestCase;" + + " public class StaticInnerTestCase{" + + " public static class Sadf extends TestCase {" + + " public Sadf() {" + + " super();" + + " }" + + " public Sadf(String name) {" + + " super(name);" + + " }" + + " public void testX() {" + + " assertTrue(\"this is true\", true);" + + " }" + + " public void testY() {" + + " assertFalse(\"this is false\", false);" + + " }" + + " }" + + "}"; + + private static final String MULTI_CLASSES_IN_FILE_TEXT = + "import junit.framework.TestCase;" + + " class A { } " + + " class B /* with syntax error */ { public void foo(int x) { } } " + + " public class DJTest extends TestCase { " + + " public void testAB() { assertTrue(\"this is true\", true); } " + + " }"; + + + /** Tests that a JUnit file with no errors is reported to have no errors. + * @throws Exception if something goes wrong + */ + protected void testNoJUnitErrors_NOJOIN(boolean testInParallel) throws Exception { + _log.log("----testNoJUnitErrors-----"); +// Utilities.show("Running testNoJUnitErrors"); + + final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); + final File file = new File(_tempDir, "MonkeyTestPass.java"); + saveFile(doc, new FileSelector(file)); + JUnitTestListener listener = new JUnitTestListener(); + _model.addListener(listener); + + listener.compile(doc); // synchronously compiles doc + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel( testInParallel); + listener.runJUnit(doc); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + listener.assertJUnitStartCount(1); + + _log.log("errors: " + _model.getJUnitModel().getJUnitErrorModel()); + + listener.assertNonTestCaseCount(0); + assertEquals("test case should have no errors reported", 0, + _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + + _model.removeListener(listener); + _log.log("testNoJUnitErrors completed"); + } + + /** Tests that a JUnit file with an error is reported to have an error. + * @throws Exception if something goes wrong + */ + protected void testOneJUnitError_NOJOIN(boolean testInParallel) throws Exception { + _log.log("----testOneJUnitError-----"); +// Utilities.show("Running testOneJUnitError"); + + final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_FAIL_TEXT); + final File file = new File(_tempDir, "MonkeyTestFail.java"); + saveFile(doc, new FileSelector(file)); + JUnitTestListener listener = new JUnitTestListener(); + _model.addListener(listener); + + listener.compile(doc); + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel( testInParallel); + listener.runJUnit(_model.getJUnitModel()); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + assertEquals("test case has one error reported", 1, _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + _model.removeListener(listener); + + _log.log("testOneJUnitError completed"); + } + + /** Tests that a JUnit file with an error is reported to have an error. + * @throws Exception if something goes wrong + */ + protected void testElspethOneJUnitError_NOJOIN(boolean testInParallel) throws Exception { + _log.log("----testElspethOneJUnitError-----"); +// Utilities.show("Running testElspethOneJunitError"); + + OpenDefinitionsDocument doc = setupDocument(ELSPETH_ERROR_TEXT); + final File file = new File(_tempDir, "Elspeth.java"); + saveFile(doc, new FileSelector(file)); + JUnitTestListener listener = new JUnitTestListener(); + _model.addListener(listener); + + listener.compile(doc); + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel( testInParallel); + listener.runJUnit(doc); + + JUnitErrorModel junitErrorModel = _model.getJUnitModel().getJUnitErrorModel(); + assertEquals("test case has one error reported", 1, junitErrorModel.getNumErrors()); + assertTrue("first error should be an error not a warning", !junitErrorModel.getError(0).isWarning()); + _model.removeListener(listener); + + _log.log("testElspethOneJUnitError completed"); + } + + + /** Tests that the ui is notified to put up an error dialog if JUnit is run on a non-TestCase. + * @throws Exception if something goes wrong + */ + protected void testNonTestCaseError_NOJOIN(boolean testInParallel) throws Exception { + _log.log("+++Starting testNonTestCaseError"); +// Utilities.show("Running testNonTestCaseError"); + + final OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT); + final File file = new File(_tempDir, "NonTestCase.java"); + saveFile(doc, new FileSelector(file)); + + JUnitTestListener listener = new JUnitNonTestListener(); + + _model.addListener(listener); + + listener.compile(doc); + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel( testInParallel); + listener.runJUnit(doc); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + _log.log("after test"); + + // Check events fired + listener.assertJUnitStartCount(0); // JUnit is never started + listener.assertJUnitEndCount(0); // JUnit never started and hence never ended + listener.assertNonTestCaseCount(1); + listener.assertJUnitSuiteStartedCount(0); + listener.assertJUnitTestStartedCount(0); + listener.assertJUnitTestEndedCount(0); + _model.removeListener(listener); + + _log.log("testNonTestCaseError completed"); + } + + /** Tests that the UI is notified to put up an error dialog if JUnit is run on a non-public TestCase. + * @throws Exception if something goes wrong + */ + protected void testResultOfNonPublicTestCase_NOJOIN(boolean testInParallel) throws Exception { + _log.log("----testResultOfNonPublicTestCase-----"); +// Utilities.show("Running testResultOfNonPublicTestCase"); + + final OpenDefinitionsDocument doc = setupDocument(NONPUBLIC_TEXT); + final File file = new File(_tempDir, "NonPublic.java"); + saveFile(doc, new FileSelector(file)); + + JUnitTestListener listener = new JUnitTestListener(); + + _model.addListener(listener); + + listener.compile(doc); + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel( testInParallel); + listener.runJUnit(doc); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + _log.log("After test"); + + // Check events fired + listener.assertJUnitStartCount(1); + listener.assertJUnitEndCount(1); + + assertEquals("test case has one error reported", 1, _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + _model.removeListener(listener); + + _log.log("testResultOfNonPublicTestCase completed"); + } + + /* This test has become inconsistent with DrJava behavior. If a document's file no longer exists and no class file + * exists, DrJava will detect that there is no valid class file for the document and ask the user to compile the + * file + */ +// public void testDoNotRunJUnitIfFileHasBeenMoved() throws Exception { +// if (printMessages) System.err.println("----testDoNotRunJUnitIfFileHasBeenMoved-----"); +//// Utilities.show("Running testDoNotRunJUnitIfFileHasBeenMoved"); +// +// final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); +// final File file = new File(_tempDir, "MonkeyTestPass.java"); +// doc.saveFile(new FileSelector(file)); +// +// JUnitTestListener listener = new JUnitTestListener(); +// +// _model.addListener(listener); +// file.delete(); +// +// listener.runJUnit(doc); +// +// listener.assertJUnitStartCount(0); +// listener.assertJUnitTestStartedCount(0); +// +// _model.removeListener(listener); +// _log.log("testDoNotRunJUnitIfFileHasBeenMoved completed"); +// } + + /** Tests a document that has no corresponding class file. + * @throws Exception if something goes wrong + */ + protected void testNoClassFile(boolean testInParallel) throws Exception { + _log.log("----testNoClassFile-----"); +// Utilities.show("Running testNoClassFile"); + + final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); + final File file = new File(_tempDir, "MonkeyTestPass.java"); + saveFile(doc, new FileSelector(file)); + + JUnitTestListener listener = new JUnitCompileBeforeTestListener(); + + _model.addListener(listener); + +// Utilities.show("calling _runJunit in testNoClassFile"); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(doc); +// Utilities.showDebug("Junit run completed"); + + _log.log("after test"); + listener.assertCompileBeforeJUnitCount(1); + listener.assertNonTestCaseCount(0); + listener.assertJUnitStartCount(1); + listener.assertJUnitEndCount(1); + listener.assertJUnitSuiteStartedCount(1); + listener.assertJUnitTestStartedCount(1); + listener.assertJUnitTestEndedCount(1); + _model.removeListener(listener); + _log.log("testNoClassFile completed"); + } + + // Commented out because MultiThreadedTestCase objects to the RemoteException thrown by auxiliary unit testing thread + // after resetInteractions kills the slave JVM. + /** Tests that an infinite loop in a test case can be aborted by clicking the Reset button. + * @throws Exception if something goes wrong + */ + protected void testInfiniteLoop_NOJOIN(boolean testInParallel) throws Exception { + _log.log("----testInfiniteLoop-----"); +// Utilities.show("Running testInfiniteLoop"); + + final OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_INFINITE_TEXT); + final File file = new File(_tempDir, "MonkeyTestInfinite.java"); + saveFile(doc, new FileSelector(file)); + + JUnitTestListener listener = new JUnitTestListener(false) { + public void junitSuiteStarted(int numTests) { + assertEquals("should run 1 test", 1, numTests); + synchronized(this) { junitSuiteStartedCount++; } + // kill the infinite test once testSuiteProcessing starts + _model.resetInteractions(new File(System.getProperty("user.dir"))); + } + }; + + _model.addListener(listener); + listener.compile(doc); + + _log.log("Compilation of infinite loop completed"); + + if (_model.getCompilerModel().getNumErrors() > 0) { + fail("compile failed: " + getCompilerErrorString()); + } + listener.checkCompileOccurred(); + + _log.log("CheckCompile completed"); +// _model.removeListener(listener); +// +// _model.addListener(listener2); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.logJUnitStart(); + try { + _log.log("Starting JUnit"); + doc.startJUnit(); + listener.waitJUnitDone(); + // this waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + // auxiliary thread silently swallows the exception and terminates. + } + catch (Exception e) { fail("Aborting unit testing runs recovery code in testing thread; no exception is thrown"); } + + listener.waitResetDone(); // reset should occur when test suite is started + + _log.log("ResetDone"); + + _log.log("after test"); + listener.assertJUnitStartCount(1); + _model.removeListener(listener); + listener.assertJUnitEndCount(1); // Testing was aborted after junitStarted(); junitEnded called in recovery code + _log.log("Reached Test End"); + _log.log("testInfiniteLoop completed"); + } + + /** Tests that when a JUnit file with no errors, after being saved and compiled, + * has it's contents replaced by a test that should fail, will pass all tests. + * @throws Exception if something goes wrong + */ + protected void testUnsavedAndUnCompiledChanges(boolean testInParallel) throws Exception { + _log.log("-----testUnsavedAndUnCompiledChanges-----"); + + OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); + final File file = new File(_tempDir, "MonkeyTestPass.java"); + saveFile(doc, new FileSelector(file)); + + List docs = _model.getSortedOpenDefinitionsDocuments(); + + final OpenDefinitionsDocument untitled = docs.get(0); + + _log.log("Untitled file is named: " + untitled.getName()); + + Utilities.invokeAndWait(new Runnable() { + public void run() { + untitled.quitFile(); + _model.closeFileWithoutPrompt(untitled); + } + }); + + // set up test listener for compile command; automatically checks that compilation is performed + JUnitTestListener listener = new JUnitCompileBeforeTestListener(); + _model.addListener(listener); + + testStartCompile(doc); + + _log.log("Ordinary compile completed"); + listener.waitCompileDone(); + + listener.resetCompileCounts(); + + changeDocumentText(MONKEYTEST_PASS_ALT_TEXT, doc); + _log.log("document changed; modifiedSinceSave = " + doc.isModifiedSinceSave()); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(doc); + _log.log("JUnit completed"); + + /* Unsaved document forces both saveBeforeCompile and compileBeforeTest */ + + listener.assertSaveBeforeCompileCount(1); + listener.assertCompileBeforeJUnitCount(1); + listener.assertNonTestCaseCount(0); + listener.assertJUnitStartCount(1); + listener.assertJUnitEndCount(1); + listener.assertJUnitSuiteStartedCount(1); + listener.assertJUnitTestStartedCount(1); + listener.assertJUnitTestEndedCount(1); + + _log.log("after test"); + _model.removeListener(listener); + + assertEquals("test case should have no errors reported after modifying", 0, + _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + + saveFile(doc, new FileSelector(file)); + + listener = new JUnitTestListener(); + _model.addListener(listener); + + + assertEquals("test case should have no errors reported after saving", 0, + _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + _model.removeListener(listener); + + _log.log("testUnsavedAndUnCompiledChanges completed"); + } + + /** Verifies that we get a nonTestCase event and that opening a single test file enables testing. + * @throws Exception if something goes wrong + */ + protected void safeJUnitAllWithNoValidTests(boolean testInParallel) throws Exception { + + _log.log("-----testJUnitAllWithNoValidTests-----"); + + JUnitNonTestListener listener = new JUnitNonTestListener(true); + _model.addListener(listener); + _model.getJUnitModel().setRunTestParallel(testInParallel); + + listener.runJUnit(_model.getJUnitModel()); + + listener.assertNonTestCaseCount(1); + listener.assertJUnitSuiteStartedCount(0); + listener.assertJUnitTestStartedCount(0); + listener.assertJUnitTestEndedCount(0); + _model.removeListener(listener); + + JUnitCompileBeforeTestListener listener2 = new JUnitCompileBeforeTestListener(); + _model.addListener(listener2); + OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT); + File file = new File(_tempDir, "NonTestCase.java"); + _log.log("-----> file = " + file + " -- canWrite() = " + file.canWrite() + " -- exists() = " + file.exists()); + saveFile(doc, new FileSelector(file)); + + listener2.compile(doc); + listener2.checkCompileOccurred(); + + listener2.resetCompileCounts(); + + // Opending Test + File file2 = new File(_tempDir, "MonkeyTestPass.java"); + OpenDefinitionsDocument doc2 = setupDocument(MONKEYTEST_PASS_TEXT); + saveFile(doc2, new FileSelector(file2)); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener2.runJUnit(_model.getJUnitModel()); + + listener2.assertNonTestCaseCount(0); + listener2.assertJUnitSuiteStartedCount(1); + listener2.assertJUnitTestStartedCount(1); + listener2.assertJUnitTestEndedCount(1); + _model.removeListener(listener2); + + _log.log("testJUnitAllWithNoValidTests completed"); + } + + /** Tests that junit all works with one or two test cases that should pass. + * @throws Exception if something goes wrong + */ + protected void safeJUnitAllWithNoErrors(boolean testInParallel) throws Exception { +// _log.log("Starting testJUnitAllWithNoErrors"); + +// final OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT); +// final File file = new File(_tempDir, "NonTestCase.java"); +// saveFile(doc, new FileSelector(file)); +// +// JUnitTestListener listener = new JUnitNonTestListener(true); +// +// _model.addListener(listener); +// +// listener.compile(doc); +// listener.checkCompileOccurred(); +// +// _log.log("Compiled first doc"); +// + OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_PASS_TEXT); + File file = new File(_tempDir, "MonkeyTestPass.java"); + saveFile(doc, new FileSelector(file)); + JUnitTestListener listener = new JUnitNonTestListener(true); + _model.addListener(listener); + listener.compile(doc); + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(_model.getJUnitModel()); + + listener.assertNonTestCaseCount(0); + listener.assertJUnitSuiteStartedCount(1); + listener.assertJUnitTestStartedCount(1); + listener.assertJUnitTestEndedCount(1); + _model.removeListener(listener); + + doc = setupDocument(HAS_MULTIPLE_TESTS_PASS_TEXT); + file = new File(_tempDir, "HasMultipleTestsPass.java"); + saveFile(doc, new FileSelector(file)); + + listener = new JUnitNonTestListener(true); + _model.addListener(listener); + + listener.compile(doc); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(_model.getJUnitModel()); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + listener.assertNonTestCaseCount(0); + listener.assertJUnitSuiteStartedCount(1); + listener.assertJUnitTestStartedCount(3); + listener.assertJUnitTestEndedCount(3); + _model.removeListener(listener); + + _log.log("testJUnitAllWithNoErrors completed"); + } + + /** Tests that junit all works with test cases that do not pass. + * @throws Exception if something goes wrong + */ + protected void safeJUnitAllWithErrors(boolean testInParallel) throws Exception { + + _log.log("-----testJUnitAllWithErrors-----"); + + OpenDefinitionsDocument doc = setupDocument(MONKEYTEST_ERROR_TEXT); + OpenDefinitionsDocument doc2 = setupDocument(MONKEYTEST_FAIL_TEXT); + File file = new File(_tempDir, "MonkeyTestError.java"); + File file2 = new File(_tempDir, "MonkeyTestFail.java"); + saveFile(doc, new FileSelector(file)); + saveFile(doc2, new FileSelector(file2)); + JUnitNonTestListener listener = new JUnitNonTestListener(true); + _model.addListener(listener); + listener.compile(doc); + listener.checkCompileOccurred(); + listener.resetCompileCounts(); + listener.compile(doc2); + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(_model.getJUnitModel()); + + listener.assertNonTestCaseCount(0); + listener.assertJUnitSuiteStartedCount(1); + listener.assertJUnitTestStartedCount(2); + listener.assertJUnitTestEndedCount(2); + _model.removeListener(listener); + + JUnitErrorModel junitErrorModel = _model.getJUnitModel().getJUnitErrorModel(); + assertEquals("test case has one error reported", 2, junitErrorModel.getNumErrors()); + + assertTrue("first error should be an error", junitErrorModel.getError(0).isWarning()); + assertFalse("second error should be a failure", junitErrorModel.getError(1).isWarning()); + + _log.log("testJUnitAllWithErrors completed"); + } + + /** Tests that junit all works with one or two test cases that should pass. + * @throws Exception if something goes wrong + */ + protected void safeJUnitStaticInnerClass(boolean testInParallel) throws Exception { + _log.log("-----testJUnitAllWithStaticInnerClass-----"); + + OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT); + OpenDefinitionsDocument doc2 = setupDocument(STATIC_INNER_TEST_TEXT); + File file = new File(_tempDir, "NonTestCase.java"); + File file2 = new File(_tempDir, "StaticInnerTestCase.java"); + saveFile(doc, new FileSelector(file)); + saveFile(doc2, new FileSelector(file2)); + + JUnitNonTestListener listener = new JUnitNonTestListener(true); + _model.addListener(listener); + listener.compile(doc); + listener.checkCompileOccurred(); + listener.resetCompileCounts(); + listener.compile(doc2); + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(_model.getJUnitModel()); + + listener.assertNonTestCaseCount(0); + listener.assertJUnitSuiteStartedCount(1); + listener.assertJUnitTestStartedCount(2); + listener.assertJUnitTestEndedCount(2); + _model.removeListener(listener); + _log.log("----testJUnitAllWithNoErrors-----"); + + _log.log("testJUnitStaticInnerClass completed"); + } + + /** Tests that testing an uncompiled but correct group of files will first compile and then run test. */ + public class JUnitCompileBeforeTestListener extends JUnitTestListener { + + /* Method copied by _mainListener in MainFrame. */ + public void compileBeforeJUnit(final CompilerListener testAfterCompile, List outOfSync) { + _log.log("compileBeforeJUnit called in listener " + this); + synchronized(this) { compileBeforeJUnitCount++; } + // Compile all open source files + _model.getCompilerModel().addListener(testAfterCompile); // listener removes itself + _log.log("Calling _compileAll()"); + try { _model.getCompilerModel().compileAll(); /* instead of invoking MainFrame._compileAll() */ } + catch(IOException e) { fail("Compile step generated IOException"); } + + _log.log("Compilation finished"); + } + + public void saveBeforeCompile() { + _log.log("saveBeforeCompile called in " + this); + synchronized(this) { saveBeforeCompileCount++; } + /** Assumes that DrJava is in flat file mode! */ + saveAllFiles(_model, new FileSaveSelector() { + public File getFile() { throw new UnexpectedException ("Test should not ask for save file name"); } + public boolean warnFileOpen(File f) { return false; } + public boolean verifyOverwrite(File f) { return true; } + public boolean shouldSaveAfterFileMoved(OpenDefinitionsDocument doc, File oldFile) { return false; } + public boolean shouldUpdateDocumentState() { return true; } + }); + } + public void fileSaved(OpenDefinitionsDocument doc) { } + } + + /** Tests that when a JUnit file with no errors is compiled and then modified to contain + * an error does not pass unit testing (by running correct class files). + * @throws Exception if something goes wrong + */ + protected void testCorrectFilesAfterIncorrectChanges_NOJOIN(boolean testInParallel) throws Exception { + _log.log("----testCorrectFilesAfterIncorrectChanges-----"); + +// OpenDefinitionsDocument doc0 = setupDocument(NON_TESTCASE_TEXT); +// JUnitNonTestListener listener0 = new JUnitNonTestListener(true); +// File file = new File(_tempDir, "NonTestCase.java"); +// saveFile(doc0, new FileSelector(file)); +// _model.addListener(listener0); +// +// listener0.compile(doc0); +// listener0.checkCompileOccurred(); +// _model.removeListener(listener0); +////What is the preceding code segment supposed to test; it has already been done! + + final OpenDefinitionsDocument doc1 = setupDocument(MULTI_CLASSES_IN_FILE_TEXT); + final File file = new File(_tempDir, "DJTest.java"); + saveFile(doc1, new FileSelector(file)); + _log.log("In testCorrectFilesAfterIncorrectChanges, DJTest.java = \n" + doc1.getText()); + + final JUnitNonTestListener listener1 = new JUnitNonTestListener(true); + _model.addListener(listener1); + listener1.compile(doc1); + listener1.checkCompileOccurred(); + assertCompileErrorsPresent(false); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener1.runJUnit(_model.getJUnitModel()); + listener1.assertJUnitSuiteStartedCount(1); + listener1.assertJUnitTestStartedCount(1); + listener1.assertJUnitTestEndedCount(1); + listener1.assertNonTestCaseCount(0); + _model.removeListener(listener1); + doc1.remove(87,4); + + JUnitTestListener listener2 = new JUnitCompileBeforeTestListener(); + _model.addListener(listener2); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener2.runJUnit(doc1); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + _log.log("after test"); + listener2.assertCompileBeforeJUnitCount(1); + listener2.assertNonTestCaseCount(1); + listener2.assertJUnitStartCount(0); + listener2.assertJUnitEndCount(0); + listener2.assertJUnitSuiteStartedCount(0); + listener2.assertJUnitTestStartedCount(0); + listener2.assertJUnitTestEndedCount(0); + _model.removeListener(listener2); + _log.log("testCorrectFilesAfterIncorrectChanges completed"); + } + + + /** Tests if a JUnit4 style unit test works. + * @throws Exception if something goes wrong + */ + protected void testJUnit4StyleTestWorks_NOJOIN(boolean testInParallel) throws Exception { + + _log.log("----testJUnit4StyleTestWorks-----"); + + File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4StyleTest.java"); + final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); + + final File file = new File(_tempDir, "JUnit4StyleTest.java"); + saveFile(doc, new FileSelector(file)); + JUnitTestListener listener = new JUnitTestListener(); + _model.addListener(listener); + + listener.compile(doc); // synchronously compiles doc + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(doc); + _log.log("errors: " + Arrays.toString(_model.getJUnitModel().getJUnitErrorModel().getErrors())); + + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + listener.assertJUnitStartCount(1); + + + listener.assertNonTestCaseCount(0); + assertEquals("test case should have no errors reported", 0, + _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + + _model.removeListener(listener); + _log.log("----testJUnit4StyleTestWorks completed"); + } + + /** Tests to see if a JUnit4 style test with multiple test cases passes. + * @throws Exception if something goes wrong + */ + protected void testJUnit4MultiTest_NOJOIN(boolean testInParallel) throws Exception { + + _log.log("----testJUnit4MultiTest-----"); + + File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4MultiTest.java"); + final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); + + final File file = new File(_tempDir, "JUnit4MultiTest.java"); + saveFile(doc, new FileSelector(file)); + JUnitTestListener listener = new JUnitTestListener(); + _model.addListener(listener); + + listener.compile(doc); // synchronously compiles doc + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnitWithoutWait(doc); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + _log.log("errors: " + Arrays.toString(_model.getJUnitModel().getJUnitErrorModel().getErrors())); + + //listener.assertJUnitStartCount(1); + + //listener.assertNonTestCaseCount(0); + assertEquals("test case should have no errors reported", 0, + _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + + _model.removeListener(listener); + _log.log("testJUnit4SMultiTest completed"); + } + + + /** Tests to see if a JUnit4 style test with no test cases will not run. + * @throws Exception if something goes wrong + */ + protected void testJUnit4NoTest_NOJOIN(boolean testInParallel) throws Exception { + _log.log("----testJUnit4NoTest-----"); + + File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4NoTest.java"); + final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); + final File file = new File(_tempDir, "JUnit4NoTest.java"); + saveFile(doc, new FileSelector(file)); + + JUnitTestListener listener = new JUnitNonTestListener(); + + _model.addListener(listener); + + listener.compile(doc); + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(doc); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + _log.log("after test"); + + // Check events fired + listener.assertJUnitStartCount(0); // JUnit is never started + listener.assertJUnitEndCount(0); // JUnit never started and hence never ended + listener.assertNonTestCaseCount(1); + listener.assertJUnitSuiteStartedCount(0); + listener.assertJUnitTestStartedCount(0); + listener.assertJUnitTestEndedCount(0); + _model.removeListener(listener); + + _log.log("testJUnit4NoTest completed"); + } + + /** Tests to see if a JUnit4 style test with a test method and multiple nonTest methods will run. + * @throws Exception if something goes wrong + */ + protected void testJUnit4TwoMethod1Test_NOJOIN(boolean testInParallel) throws Exception { + + _log.log("----testJUnit4TwoMethod1Test-----"); + + File file0 = new File("testFiles/GlobalModelJUnitTestFiles/JUnit4TwoMethod1Test.java"); + final OpenDefinitionsDocument doc = setupDocument((_model._createOpenDefinitionsDocument(file0)).getText()); + + final File file = new File(_tempDir, "JUnit4TwoMethod1Test.java"); + saveFile(doc, new FileSelector(file)); + JUnitTestListener listener = new JUnitTestListener(); + _model.addListener(listener); + + listener.compile(doc); // synchronously compiles doc + listener.checkCompileOccurred(); + _model.getJUnitModel().setRunTestParallel(testInParallel); + listener.runJUnit(doc); + // runJUnit waits until the thread started in DefaultJUnitModel._rawJUnitOpenDefDocs has called notify + + _log.log("errors: " + Arrays.toString(_model.getJUnitModel().getJUnitErrorModel().getErrors())); + + listener.assertJUnitStartCount(1); + listener.assertNonTestCaseCount(0); + assertEquals("test case should have no errors reported", 0, + _model.getJUnitModel().getJUnitErrorModel().getNumErrors()); + + _model.removeListener(listener); + _log.log("testJUnit4TwoMethod1Test completed"); + } +} diff --git a/drjava/src/edu/rice/cs/drjava/model/GlobalModelTestCase.java b/drjava/src/edu/rice/cs/drjava/model/GlobalModelTestCase.java index bb02366fc..cb1d155fa 100644 --- a/drjava/src/edu/rice/cs/drjava/model/GlobalModelTestCase.java +++ b/drjava/src/edu/rice/cs/drjava/model/GlobalModelTestCase.java @@ -1288,6 +1288,13 @@ public void runJUnit(JUnitModel jm) throws IOException, ClassNotFoundException, waitJUnitDone(); } + public void runJUnitWithoutWait(JUnitModel jm) throws IOException, ClassNotFoundException, InterruptedException { + logJUnitStart(); +// _log.log("Starting JUnit"); + jm.junitAll(); + + } + public void waitJUnitDone() throws InterruptedException { synchronized(_junitLock) { while (! _junitDone) { _junitLock.wait(); } } } @@ -1352,6 +1359,13 @@ public synchronized void resetJUnitCounts() { _log.log("junitEnded() called; notifying JUnitDone"); _notifyJUnitDone(); } + public void runJUnitWithoutWait(OpenDefinitionsDocument doc) throws ClassNotFoundException, IOException { + logJUnitStart(); + // System.err.println("Starting JUnit on " + doc); + doc.startJUnit(); + // System.err.println("JUnit Started on " + doc); + + } } /** Listener class for failing JUnit invocation. */ diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java b/drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java index 576aeef96..443dedbb0 100644 --- a/drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java +++ b/drjava/src/edu/rice/cs/drjava/model/junit/DefaultJUnitModel.java @@ -92,7 +92,7 @@ public class DefaultJUnitModel implements JUnitModel, JUnitModelCallback { /** log for use in debugging */ //needtodo - private static Log _log = new Log("DefaultJUnitModel.txt", true); + private static Log _log = new Log("DefaultJUnitModel.txt", false); /** Manages listeners to this model. */ private final JUnitEventNotifier _notifier = new JUnitEventNotifier(); diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java index f5cf785dc..be0036b99 100644 --- a/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java +++ b/drjava/src/edu/rice/cs/drjava/model/junit/JUnitParallelTestManager.java @@ -103,7 +103,7 @@ * @version $Id$ */ public class JUnitParallelTestManager extends JUnitTestManager{ - public static Log _log = new Log("JUnitParallelTestManager.txt", true); + public static Log _log = new Log("JUnitParallelTestManager.txt", false); diff --git a/drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java b/drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java index f0a0f6743..23c2575a9 100644 --- a/drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java +++ b/drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java @@ -8,6 +8,8 @@ import org.junit.runner.notification.RunListener; import org.junit.runner.notification.RunNotifier; +import edu.rice.cs.util.Log; + /** * This class is used to run test in parallel. * It uses ParallelComputer to parallel test method and test class @@ -15,7 +17,8 @@ * */ public class MyJUnitCore extends JUnitCore { - + public static Log _log = new Log("MyJUnitCore.txt", false); + private final RunNotifier runNotifier = new RunNotifier(); /** @@ -25,6 +28,7 @@ public class MyJUnitCore extends JUnitCore { * @return result of running these test case */ public Result parallelRunClasses(RunListener listener, Class[] classes) { + _log.log("start parallelRunClasses"); Runner runner = Request.classes(new ParallelComputer(true, true), classes).getRunner(); Result result = new Result(); RunListener resultListener = result.createListener(); diff --git a/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java b/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java index 97ad4c987..562c69c2c 100644 --- a/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java +++ b/drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java @@ -543,7 +543,10 @@ public List findTestClasses(List classNames, * and does not involve mutable local state. * @return false if no test suite is cached; true otherwise */ - public boolean runTestSuite(Boolean runTestParallel) throws RemoteException { return _junitTestManager.runTestSuite(runTestParallel); } + public boolean runTestSuite(Boolean runTestParallel) throws RemoteException { + _log.log("runTestSuite with runTestParallel= "+runTestParallel); + return _junitTestManager.runTestSuite(runTestParallel); + } /** Notifies Main JVM that JUnit has been invoked on a non TestCase class. Unsynchronized because it contains a * remote call and does not involve mutable local state. diff --git a/drjava/src/edu/rice/cs/util/LogTest.java b/drjava/src/edu/rice/cs/util/LogTest.java index 190a13f1e..e52f2cb6d 100644 --- a/drjava/src/edu/rice/cs/util/LogTest.java +++ b/drjava/src/edu/rice/cs/util/LogTest.java @@ -219,7 +219,7 @@ public void testExceptionPrinting() throws IOException { private static final int DELAY = 100; //TODO task - private static final Log ltl = new Log("logtest.txt", true); + private static final Log ltl = new Log("logtest.txt", false); /** Attempts to test Log's behavior when called concurrently from several sources. Spawns NUM_THREADS LogTestThreads * (see above)that wait a random number between 0 and DELAY milliseconds and then log a message. The function tests @@ -248,7 +248,6 @@ public void testConcurrentWrites() throws IOException, InterruptedException { Date now = new Date(); String s0 = fin.readLine(); Date time0 = log3.parse(s0); - //TODOtask ltl.log("earlier = " + earlier); ltl.log("now = " + now); ltl.log("time0 = " + time0); @@ -260,7 +259,6 @@ public void testConcurrentWrites() throws IOException, InterruptedException { for (int i = 0; i < NUM_THREADS; i++) { String s1 = fin.readLine(); Date time1 = log3.parse(s1); - //TODOtask ltl.log("time1 = " + time1); assertTrue("Date of message not after 'earlier' and before 'now'", withinTolerance(earlier, time1, now)); assertTrue("Date of message not after 'previous time' and before 'now'", withinTolerance(time0, time1, now));