diff --git a/drjava/build.xml b/drjava/build.xml index 15c8ff541..c0ef01e8c 100644 --- a/drjava/build.xml +++ b/drjava/build.xml @@ -61,13 +61,17 @@ - + + + + - - + + - + @@ -345,7 +349,19 @@ - + + + + + + + + + + + + + @@ -425,8 +441,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -454,7 +512,7 @@ - + @@ -463,6 +521,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/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 4dc11b328..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,649 +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"); - } - - /** 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) { } + testResultOfNonPublicTestCase_NOJOIN(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 { - _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 c39dfc762..443dedbb0 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", false); /** 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..be0036b99 --- /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", false); + + + + /** 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..23c2575a9 --- /dev/null +++ b/drjava/src/edu/rice/cs/drjava/model/junit/MyJUnitCore.java @@ -0,0 +1,50 @@ +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; + +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 + * @author zhexin + * + */ +public class MyJUnitCore extends JUnitCore { + public static Log _log = new Log("MyJUnitCore.txt", false); + + 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) { + _log.log("start parallelRunClasses"); + 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..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 @@ -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,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() throws RemoteException { return _junitTestManager.runTestSuite(); } + 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/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..e52f2cb6d 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", 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 @@ -247,9 +248,9 @@ 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); + 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 +259,7 @@ public void testConcurrentWrites() throws IOException, InterruptedException { for (int i = 0; i < NUM_THREADS; i++) { String s1 = fin.readLine(); Date time1 = log3.parse(s1); + 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));