diff options
author | Yohann Roussel <yroussel@google.com> | 2014-03-19 16:25:37 +0100 |
---|---|---|
committer | Yohann Roussel <yroussel@google.com> | 2014-03-20 15:13:33 +0100 |
commit | 4eceb95409e844fdc33c9c706e1dc307bfd40303 (patch) | |
tree | ee9f4f3fc79f757c79081c336bce4f1782c6ccd8 /junit4/src | |
parent | 3d2402901b1a6462e2cf47a6fd09711f327961c3 (diff) | |
download | toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.zip toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.gz toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.bz2 |
Initial Jack import.
Change-Id: I953cf0a520195a7187d791b2885848ad0d5a9b43
Diffstat (limited to 'junit4/src')
322 files changed, 23852 insertions, 0 deletions
diff --git a/junit4/src/main/java/junit/extensions/ActiveTestSuite.java b/junit4/src/main/java/junit/extensions/ActiveTestSuite.java new file mode 100644 index 0000000..0623565 --- /dev/null +++ b/junit4/src/main/java/junit/extensions/ActiveTestSuite.java @@ -0,0 +1,70 @@ +package junit.extensions; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +/** + * A TestSuite for active Tests. It runs each + * test in a separate thread and waits until all + * threads have terminated. + * -- Aarhus Radisson Scandinavian Center 11th floor + */ +public class ActiveTestSuite extends TestSuite { + private volatile int fActiveTestDeathCount; + + public ActiveTestSuite() { + } + + public ActiveTestSuite(Class<? extends TestCase> theClass) { + super(theClass); + } + + public ActiveTestSuite(String name) { + super (name); + } + + public ActiveTestSuite(Class<? extends TestCase> theClass, String name) { + super(theClass, name); + } + + @Override + public void run(TestResult result) { + fActiveTestDeathCount= 0; + super.run(result); + waitUntilFinished(); + } + + @Override + public void runTest(final Test test, final TestResult result) { + Thread t= new Thread() { + @Override + public void run() { + try { + // inlined due to limitation in VA/Java + //ActiveTestSuite.super.runTest(test, result); + test.run(result); + } finally { + ActiveTestSuite.this.runFinished(); + } + } + }; + t.start(); + } + + synchronized void waitUntilFinished() { + while (fActiveTestDeathCount < testCount()) { + try { + wait(); + } catch (InterruptedException e) { + return; // ignore + } + } + } + + synchronized public void runFinished() { + fActiveTestDeathCount++; + notifyAll(); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/extensions/RepeatedTest.java b/junit4/src/main/java/junit/extensions/RepeatedTest.java new file mode 100644 index 0000000..3b687a5 --- /dev/null +++ b/junit4/src/main/java/junit/extensions/RepeatedTest.java @@ -0,0 +1,38 @@ +package junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; + +/** + * A Decorator that runs a test repeatedly. + * + */ +public class RepeatedTest extends TestDecorator { + private int fTimesRepeat; + + public RepeatedTest(Test test, int repeat) { + super(test); + if (repeat < 0) + throw new IllegalArgumentException("Repetition count must be >= 0"); + fTimesRepeat= repeat; + } + + @Override + public int countTestCases() { + return super.countTestCases() * fTimesRepeat; + } + + @Override + public void run(TestResult result) { + for (int i= 0; i < fTimesRepeat; i++) { + if (result.shouldStop()) + break; + super.run(result); + } + } + + @Override + public String toString() { + return super.toString() + "(repeated)"; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/extensions/TestDecorator.java b/junit4/src/main/java/junit/extensions/TestDecorator.java new file mode 100644 index 0000000..d9ae474 --- /dev/null +++ b/junit4/src/main/java/junit/extensions/TestDecorator.java @@ -0,0 +1,43 @@ +package junit.extensions; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestResult; + +/** + * A Decorator for Tests. Use TestDecorator as the base class for defining new + * test decorators. Test decorator subclasses can be introduced to add behaviour + * before or after a test is run. + * + */ +public class TestDecorator extends Assert implements Test { + protected Test fTest; + + public TestDecorator(Test test) { + fTest= test; + } + + /** + * The basic run behaviour. + */ + public void basicRun(TestResult result) { + fTest.run(result); + } + + public int countTestCases() { + return fTest.countTestCases(); + } + + public void run(TestResult result) { + basicRun(result); + } + + @Override + public String toString() { + return fTest.toString(); + } + + public Test getTest() { + return fTest; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/extensions/TestSetup.java b/junit4/src/main/java/junit/extensions/TestSetup.java new file mode 100644 index 0000000..00dcd21 --- /dev/null +++ b/junit4/src/main/java/junit/extensions/TestSetup.java @@ -0,0 +1,42 @@ +package junit.extensions; + +import junit.framework.Protectable; +import junit.framework.Test; +import junit.framework.TestResult; + +/** + * A Decorator to set up and tear down additional fixture state. Subclass + * TestSetup and insert it into your tests when you want to set up additional + * state once before the tests are run. + */ +public class TestSetup extends TestDecorator { + + public TestSetup(Test test) { + super(test); + } + + @Override + public void run(final TestResult result) { + Protectable p= new Protectable() { + public void protect() throws Exception { + setUp(); + basicRun(result); + tearDown(); + } + }; + result.runProtected(this, p); + } + + /** + * Sets up the fixture. Override to set up additional fixture state. + */ + protected void setUp() throws Exception { + } + + /** + * Tears down the fixture. Override to tear down the additional fixture + * state. + */ + protected void tearDown() throws Exception { + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/extensions/package-info.java b/junit4/src/main/java/junit/extensions/package-info.java new file mode 100644 index 0000000..a1c5bb4 --- /dev/null +++ b/junit4/src/main/java/junit/extensions/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides extended functionality for JUnit v3.x. + */ +package junit.extensions;
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/Assert.java b/junit4/src/main/java/junit/framework/Assert.java new file mode 100644 index 0000000..3dcc23d --- /dev/null +++ b/junit4/src/main/java/junit/framework/Assert.java @@ -0,0 +1,296 @@ +package junit.framework; + +/** + * A set of assert methods. Messages are only displayed when an assert fails. + */ + +public class Assert { + /** + * Protect constructor since it is a static only class + */ + protected Assert() { + } + + /** + * Asserts that a condition is true. If it isn't it throws + * an AssertionFailedError with the given message. + */ + static public void assertTrue(String message, boolean condition) { + if (!condition) + fail(message); + } + /** + * Asserts that a condition is true. If it isn't it throws + * an AssertionFailedError. + */ + static public void assertTrue(boolean condition) { + assertTrue(null, condition); + } + /** + * Asserts that a condition is false. If it isn't it throws + * an AssertionFailedError with the given message. + */ + static public void assertFalse(String message, boolean condition) { + assertTrue(message, !condition); + } + /** + * Asserts that a condition is false. If it isn't it throws + * an AssertionFailedError. + */ + static public void assertFalse(boolean condition) { + assertFalse(null, condition); + } + /** + * Fails a test with the given message. + */ + static public void fail(String message) { + if (message == null) { + throw new AssertionFailedError(); + } + throw new AssertionFailedError(message); + } + /** + * Fails a test with no message. + */ + static public void fail() { + fail(null); + } + /** + * Asserts that two objects are equal. If they are not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertEquals(String message, Object expected, Object actual) { + if (expected == null && actual == null) + return; + if (expected != null && expected.equals(actual)) + return; + failNotEquals(message, expected, actual); + } + /** + * Asserts that two objects are equal. If they are not + * an AssertionFailedError is thrown. + */ + static public void assertEquals(Object expected, Object actual) { + assertEquals(null, expected, actual); + } + /** + * Asserts that two Strings are equal. + */ + static public void assertEquals(String message, String expected, String actual) { + if (expected == null && actual == null) + return; + if (expected != null && expected.equals(actual)) + return; + String cleanMessage= message == null ? "" : message; + throw new ComparisonFailure(cleanMessage, expected, actual); + } + /** + * Asserts that two Strings are equal. + */ + static public void assertEquals(String expected, String actual) { + assertEquals(null, expected, actual); + } + /** + * Asserts that two doubles are equal concerning a delta. If they are not + * an AssertionFailedError is thrown with the given message. If the expected + * value is infinity then the delta value is ignored. + */ + static public void assertEquals(String message, double expected, double actual, double delta) { + if (Double.compare(expected, actual) == 0) + return; + if (!(Math.abs(expected-actual) <= delta)) + failNotEquals(message, new Double(expected), new Double(actual)); + } + /** + * Asserts that two doubles are equal concerning a delta. If the expected + * value is infinity then the delta value is ignored. + */ + static public void assertEquals(double expected, double actual, double delta) { + assertEquals(null, expected, actual, delta); + } + /** + * Asserts that two floats are equal concerning a positive delta. If they + * are not an AssertionFailedError is thrown with the given message. If the + * expected value is infinity then the delta value is ignored. + */ + static public void assertEquals(String message, float expected, float actual, float delta) { + if (Float.compare(expected, actual) == 0) + return; + if (!(Math.abs(expected - actual) <= delta)) + failNotEquals(message, new Float(expected), new Float(actual)); + } + /** + * Asserts that two floats are equal concerning a delta. If the expected + * value is infinity then the delta value is ignored. + */ + static public void assertEquals(float expected, float actual, float delta) { + assertEquals(null, expected, actual, delta); + } + /** + * Asserts that two longs are equal. If they are not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertEquals(String message, long expected, long actual) { + assertEquals(message, new Long(expected), new Long(actual)); + } + /** + * Asserts that two longs are equal. + */ + static public void assertEquals(long expected, long actual) { + assertEquals(null, expected, actual); + } + /** + * Asserts that two booleans are equal. If they are not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertEquals(String message, boolean expected, boolean actual) { + assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual)); + } + /** + * Asserts that two booleans are equal. + */ + static public void assertEquals(boolean expected, boolean actual) { + assertEquals(null, expected, actual); + } + /** + * Asserts that two bytes are equal. If they are not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertEquals(String message, byte expected, byte actual) { + assertEquals(message, new Byte(expected), new Byte(actual)); + } + /** + * Asserts that two bytes are equal. + */ + static public void assertEquals(byte expected, byte actual) { + assertEquals(null, expected, actual); + } + /** + * Asserts that two chars are equal. If they are not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertEquals(String message, char expected, char actual) { + assertEquals(message, new Character(expected), new Character(actual)); + } + /** + * Asserts that two chars are equal. + */ + static public void assertEquals(char expected, char actual) { + assertEquals(null, expected, actual); + } + /** + * Asserts that two shorts are equal. If they are not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertEquals(String message, short expected, short actual) { + assertEquals(message, new Short(expected), new Short(actual)); + } + /** + * Asserts that two shorts are equal. + */ + static public void assertEquals(short expected, short actual) { + assertEquals(null, expected, actual); + } + /** + * Asserts that two ints are equal. If they are not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertEquals(String message, int expected, int actual) { + assertEquals(message, new Integer(expected), new Integer(actual)); + } + /** + * Asserts that two ints are equal. + */ + static public void assertEquals(int expected, int actual) { + assertEquals(null, expected, actual); + } + /** + * Asserts that an object isn't null. + */ + static public void assertNotNull(Object object) { + assertNotNull(null, object); + } + /** + * Asserts that an object isn't null. If it is + * an AssertionFailedError is thrown with the given message. + */ + static public void assertNotNull(String message, Object object) { + assertTrue(message, object != null); + } + /** + * Asserts that an object is null. If it isn't an {@link AssertionError} is + * thrown. + * Message contains: Expected: <null> but was: object + * + * @param object + * Object to check or <code>null</code> + */ + static public void assertNull(Object object) { + String message = "Expected: <null> but was: " + String.valueOf(object); + assertNull(message, object); + } + /** + * Asserts that an object is null. If it is not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertNull(String message, Object object) { + assertTrue(message, object == null); + } + /** + * Asserts that two objects refer to the same object. If they are not + * an AssertionFailedError is thrown with the given message. + */ + static public void assertSame(String message, Object expected, Object actual) { + if (expected == actual) + return; + failNotSame(message, expected, actual); + } + /** + * Asserts that two objects refer to the same object. If they are not + * the same an AssertionFailedError is thrown. + */ + static public void assertSame(Object expected, Object actual) { + assertSame(null, expected, actual); + } + /** + * Asserts that two objects do not refer to the same object. If they do + * refer to the same object an AssertionFailedError is thrown with the + * given message. + */ + static public void assertNotSame(String message, Object expected, Object actual) { + if (expected == actual) + failSame(message); + } + /** + * Asserts that two objects do not refer to the same object. If they do + * refer to the same object an AssertionFailedError is thrown. + */ + static public void assertNotSame(Object expected, Object actual) { + assertNotSame(null, expected, actual); + } + + static public void failSame(String message) { + String formatted= ""; + if (message != null) + formatted= message+" "; + fail(formatted+"expected not same"); + } + + static public void failNotSame(String message, Object expected, Object actual) { + String formatted= ""; + if (message != null) + formatted= message+" "; + fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">"); + } + + static public void failNotEquals(String message, Object expected, Object actual) { + fail(format(message, expected, actual)); + } + + public static String format(String message, Object expected, Object actual) { + String formatted= ""; + if (message != null && message.length() > 0) + formatted= message+" "; + return formatted+"expected:<"+expected+"> but was:<"+actual+">"; + } +} diff --git a/junit4/src/main/java/junit/framework/AssertionFailedError.java b/junit4/src/main/java/junit/framework/AssertionFailedError.java new file mode 100644 index 0000000..0d7802c --- /dev/null +++ b/junit4/src/main/java/junit/framework/AssertionFailedError.java @@ -0,0 +1,20 @@ +package junit.framework; + +/** + * Thrown when an assertion failed. + */ +public class AssertionFailedError extends AssertionError { + + private static final long serialVersionUID= 1L; + + public AssertionFailedError() { + } + + public AssertionFailedError(String message) { + super(defaultString(message)); + } + + private static String defaultString(String message) { + return message == null ? "" : message; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/ComparisonCompactor.java b/junit4/src/main/java/junit/framework/ComparisonCompactor.java new file mode 100644 index 0000000..bbc3ba1 --- /dev/null +++ b/junit4/src/main/java/junit/framework/ComparisonCompactor.java @@ -0,0 +1,72 @@ +package junit.framework; + +public class ComparisonCompactor { + + private static final String ELLIPSIS= "..."; + private static final String DELTA_END= "]"; + private static final String DELTA_START= "["; + + private int fContextLength; + private String fExpected; + private String fActual; + private int fPrefix; + private int fSuffix; + + public ComparisonCompactor(int contextLength, String expected, String actual) { + fContextLength= contextLength; + fExpected= expected; + fActual= actual; + } + + public String compact(String message) { + if (fExpected == null || fActual == null || areStringsEqual()) + return Assert.format(message, fExpected, fActual); + + findCommonPrefix(); + findCommonSuffix(); + String expected= compactString(fExpected); + String actual= compactString(fActual); + return Assert.format(message, expected, actual); + } + + private String compactString(String source) { + String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END; + if (fPrefix > 0) + result= computeCommonPrefix() + result; + if (fSuffix > 0) + result= result + computeCommonSuffix(); + return result; + } + + private void findCommonPrefix() { + fPrefix= 0; + int end= Math.min(fExpected.length(), fActual.length()); + for (; fPrefix < end; fPrefix++) { + if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix)) + break; + } + } + + private void findCommonSuffix() { + int expectedSuffix= fExpected.length() - 1; + int actualSuffix= fActual.length() - 1; + for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) { + if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix)) + break; + } + fSuffix= fExpected.length() - expectedSuffix; + } + + private String computeCommonPrefix() { + return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix); + } + + private String computeCommonSuffix() { + int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length()); + return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : ""); + } + + private boolean areStringsEqual() { + return fExpected.equals(fActual); + } +} diff --git a/junit4/src/main/java/junit/framework/ComparisonFailure.java b/junit4/src/main/java/junit/framework/ComparisonFailure.java new file mode 100644 index 0000000..5077993 --- /dev/null +++ b/junit4/src/main/java/junit/framework/ComparisonFailure.java @@ -0,0 +1,52 @@ +package junit.framework; + +/** + * Thrown when an assert equals for Strings failed. + * + * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com + */ +public class ComparisonFailure extends AssertionFailedError { + private static final int MAX_CONTEXT_LENGTH= 20; + private static final long serialVersionUID= 1L; + + private String fExpected; + private String fActual; + + /** + * Constructs a comparison failure. + * @param message the identifying message or null + * @param expected the expected string value + * @param actual the actual string value + */ + public ComparisonFailure (String message, String expected, String actual) { + super (message); + fExpected= expected; + fActual= actual; + } + + /** + * Returns "..." in place of common prefix and "..." in + * place of common suffix between expected and actual. + * + * @see Throwable#getMessage() + */ + @Override + public String getMessage() { + return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage()); + } + + /** + * Gets the actual string value + * @return the actual string value + */ + public String getActual() { + return fActual; + } + /** + * Gets the expected string value + * @return the expected string value + */ + public String getExpected() { + return fExpected; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/JUnit4TestAdapter.java b/junit4/src/main/java/junit/framework/JUnit4TestAdapter.java new file mode 100644 index 0000000..a05a313 --- /dev/null +++ b/junit4/src/main/java/junit/framework/JUnit4TestAdapter.java @@ -0,0 +1,85 @@ +package junit.framework; + +import java.util.List; + +import org.junit.Ignore; +import org.junit.runner.Describable; +import org.junit.runner.Description; +import org.junit.runner.Request; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.manipulation.Sortable; +import org.junit.runner.manipulation.Sorter; + +public class JUnit4TestAdapter implements Test, Filterable, Sortable, Describable { + private final Class<?> fNewTestClass; + + private final Runner fRunner; + + private final JUnit4TestAdapterCache fCache; + + public JUnit4TestAdapter(Class<?> newTestClass) { + this(newTestClass, JUnit4TestAdapterCache.getDefault()); + } + + public JUnit4TestAdapter(final Class<?> newTestClass, + JUnit4TestAdapterCache cache) { + fCache = cache; + fNewTestClass = newTestClass; + fRunner = Request.classWithoutSuiteMethod(newTestClass).getRunner(); + } + + public int countTestCases() { + return fRunner.testCount(); + } + + public void run(TestResult result) { + fRunner.run(fCache.getNotifier(result, this)); + } + + // reflective interface for Eclipse + public List<Test> getTests() { + return fCache.asTestList(getDescription()); + } + + // reflective interface for Eclipse + public Class<?> getTestClass() { + return fNewTestClass; + } + + public Description getDescription() { + Description description= fRunner.getDescription(); + return removeIgnored(description); + } + + private Description removeIgnored(Description description) { + if (isIgnored(description)) + return Description.EMPTY; + Description result = description.childlessCopy(); + for (Description each : description.getChildren()) { + Description child= removeIgnored(each); + if (! child.isEmpty()) + result.addChild(child); + } + return result; + } + + private boolean isIgnored(Description description) { + return description.getAnnotation(Ignore.class) != null; + } + + @Override + public String toString() { + return fNewTestClass.getName(); + } + + public void filter(Filter filter) throws NoTestsRemainException { + filter.apply(fRunner); + } + + public void sort(Sorter sorter) { + sorter.apply(fRunner); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/JUnit4TestAdapterCache.java b/junit4/src/main/java/junit/framework/JUnit4TestAdapterCache.java new file mode 100644 index 0000000..26175c5 --- /dev/null +++ b/junit4/src/main/java/junit/framework/JUnit4TestAdapterCache.java @@ -0,0 +1,81 @@ +/** + * + */ +package junit.framework; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; + +public class JUnit4TestAdapterCache extends HashMap<Description, Test> { + private static final long serialVersionUID = 1L; + private static final JUnit4TestAdapterCache fInstance = new JUnit4TestAdapterCache(); + + public static JUnit4TestAdapterCache getDefault() { + return fInstance; + } + + public Test asTest(Description description) { + if (description.isSuite()) + return createTest(description); + else { + if (!containsKey(description)) + put(description, createTest(description)); + return get(description); + } + } + + Test createTest(Description description) { + if (description.isTest()) + return new JUnit4TestCaseFacade(description); + else { + TestSuite suite = new TestSuite(description.getDisplayName()); + for (Description child : description.getChildren()) + suite.addTest(asTest(child)); + return suite; + } + } + + public RunNotifier getNotifier(final TestResult result, + final JUnit4TestAdapter adapter) { + RunNotifier notifier = new RunNotifier(); + notifier.addListener(new RunListener() { + @Override + public void testFailure(Failure failure) throws Exception { + result.addError(asTest(failure.getDescription()), failure.getException()); + } + + @Override + public void testFinished(Description description) + throws Exception { + result.endTest(asTest(description)); + } + + @Override + public void testStarted(Description description) + throws Exception { + result.startTest(asTest(description)); + } + }); + return notifier; + } + + public List<Test> asTestList(Description description) { + if (description.isTest()) + return Arrays.asList(asTest(description)); + else { + List<Test> returnThis = new ArrayList<Test>(); + for (Description child : description.getChildren()) { + returnThis.add(asTest(child)); + } + return returnThis; + } + } + +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/JUnit4TestCaseFacade.java b/junit4/src/main/java/junit/framework/JUnit4TestCaseFacade.java new file mode 100644 index 0000000..fd43822 --- /dev/null +++ b/junit4/src/main/java/junit/framework/JUnit4TestCaseFacade.java @@ -0,0 +1,33 @@ +/** + * + */ +package junit.framework; + +import org.junit.runner.Describable; +import org.junit.runner.Description; + +public class JUnit4TestCaseFacade implements Test, Describable { + private final Description fDescription; + + JUnit4TestCaseFacade(Description description) { + fDescription = description; + } + + @Override + public String toString() { + return getDescription().toString(); + } + + public int countTestCases() { + return 1; + } + + public void run(TestResult result) { + throw new RuntimeException( + "This test stub created only for informational purposes."); + } + + public Description getDescription() { + return fDescription; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/Protectable.java b/junit4/src/main/java/junit/framework/Protectable.java new file mode 100644 index 0000000..e143237 --- /dev/null +++ b/junit4/src/main/java/junit/framework/Protectable.java @@ -0,0 +1,14 @@ +package junit.framework; + +/** + * A <em>Protectable</em> can be run and can throw a Throwable. + * + * @see TestResult + */ +public interface Protectable { + + /** + * Run the the following method protected. + */ + public abstract void protect() throws Throwable; +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/Test.java b/junit4/src/main/java/junit/framework/Test.java new file mode 100644 index 0000000..a016ee8 --- /dev/null +++ b/junit4/src/main/java/junit/framework/Test.java @@ -0,0 +1,17 @@ +package junit.framework; + +/** + * A <em>Test</em> can be run and collect its results. + * + * @see TestResult + */ +public interface Test { + /** + * Counts the number of test cases that will be run by this test. + */ + public abstract int countTestCases(); + /** + * Runs a test and collects its result in a TestResult instance. + */ + public abstract void run(TestResult result); +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/TestCase.java b/junit4/src/main/java/junit/framework/TestCase.java new file mode 100644 index 0000000..b047ec9 --- /dev/null +++ b/junit4/src/main/java/junit/framework/TestCase.java @@ -0,0 +1,212 @@ +package junit.framework; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * A test case defines the fixture to run multiple tests. To define a test case<br/> + * <ol> + * <li>implement a subclass of <code>TestCase</code></li> + * <li>define instance variables that store the state of the fixture</li> + * <li>initialize the fixture state by overriding {@link #setUp()}</li> + * <li>clean-up after a test by overriding {@link #tearDown()}.</li> + * </ol> + * Each test runs in its own fixture so there + * can be no side effects among test runs. + * Here is an example: + * <pre> + * public class MathTest extends TestCase { + * protected double fValue1; + * protected double fValue2; + * + * protected void setUp() { + * fValue1= 2.0; + * fValue2= 3.0; + * } + * } + * </pre> + * + * For each test implement a method which interacts + * with the fixture. Verify the expected results with assertions specified + * by calling {@link junit.framework.Assert#assertTrue(String, boolean)} with a boolean. + * <pre> + * public void testAdd() { + * double result= fValue1 + fValue2; + * assertTrue(result == 5.0); + * } + * </pre> + * + * Once the methods are defined you can run them. The framework supports + * both a static type safe and more dynamic way to run a test. + * In the static way you override the runTest method and define the method to + * be invoked. A convenient way to do so is with an anonymous inner class. + * <pre> + * TestCase test= new MathTest("add") { + * public void runTest() { + * testAdd(); + * } + * }; + * test.run(); + * </pre> + * + * The dynamic way uses reflection to implement {@link #runTest()}. It dynamically finds + * and invokes a method. + * In this case the name of the test case has to correspond to the test method + * to be run. + * <pre> + * TestCase test= new MathTest("testAdd"); + * test.run(); + * </pre> + * + * The tests to be run can be collected into a TestSuite. JUnit provides + * different <i>test runners</i> which can run a test suite and collect the results. + * A test runner either expects a static method <code>suite</code> as the entry + * point to get a test to run or it will extract the suite automatically. + * <pre> + * public static Test suite() { + * suite.addTest(new MathTest("testAdd")); + * suite.addTest(new MathTest("testDivideByZero")); + * return suite; + * } + * </pre> + * @see TestResult + * @see TestSuite + */ +public abstract class TestCase extends Assert implements Test { + /** + * the name of the test case + */ + private String fName; + + /** + * No-arg constructor to enable serialization. This method + * is not intended to be used by mere mortals without calling setName(). + */ + public TestCase() { + fName= null; + } + /** + * Constructs a test case with the given name. + */ + public TestCase(String name) { + fName= name; + } + /** + * Counts the number of test cases executed by run(TestResult result). + */ + public int countTestCases() { + return 1; + } + /** + * Creates a default TestResult object + * + * @see TestResult + */ + protected TestResult createResult() { + return new TestResult(); + } + /** + * A convenience method to run this test, collecting the results with a + * default TestResult object. + * + * @see TestResult + */ + public TestResult run() { + TestResult result= createResult(); + run(result); + return result; + } + /** + * Runs the test case and collects the results in TestResult. + */ + public void run(TestResult result) { + result.run(this); + } + /** + * Runs the bare test sequence. + * @throws Throwable if any exception is thrown + */ + public void runBare() throws Throwable { + Throwable exception= null; + setUp(); + try { + runTest(); + } catch (Throwable running) { + exception= running; + } + finally { + try { + tearDown(); + } catch (Throwable tearingDown) { + if (exception == null) exception= tearingDown; + } + } + if (exception != null) throw exception; + } + /** + * Override to run the test and assert its state. + * @throws Throwable if any exception is thrown + */ + protected void runTest() throws Throwable { + assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null); + Method runMethod= null; + try { + // use getMethod to get all public inherited + // methods. getDeclaredMethods returns all + // methods of this class but excludes the + // inherited ones. + runMethod= getClass().getMethod(fName, (Class[])null); + } catch (NoSuchMethodException e) { + fail("Method \""+fName+"\" not found"); + } + if (!Modifier.isPublic(runMethod.getModifiers())) { + fail("Method \""+fName+"\" should be public"); + } + + try { + runMethod.invoke(this); + } + catch (InvocationTargetException e) { + e.fillInStackTrace(); + throw e.getTargetException(); + } + catch (IllegalAccessException e) { + e.fillInStackTrace(); + throw e; + } + } + /** + * Sets up the fixture, for example, open a network connection. + * This method is called before a test is executed. + */ + protected void setUp() throws Exception { + } + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + */ + protected void tearDown() throws Exception { + } + /** + * Returns a string representation of the test case + */ + @Override + public String toString() { + return getName() + "(" + getClass().getName() + ")"; + } + /** + * Gets the name of a TestCase + * @return the name of the TestCase + */ + public String getName() { + return fName; + } + /** + * Sets the name of a TestCase + * @param name the name to set + */ + public void setName(String name) { + fName= name; + } +} diff --git a/junit4/src/main/java/junit/framework/TestFailure.java b/junit4/src/main/java/junit/framework/TestFailure.java new file mode 100644 index 0000000..6662b1f --- /dev/null +++ b/junit4/src/main/java/junit/framework/TestFailure.java @@ -0,0 +1,58 @@ +package junit.framework; + +import java.io.PrintWriter; +import java.io.StringWriter; + + +/** + * A <code>TestFailure</code> collects a failed test together with + * the caught exception. + * @see TestResult + */ +public class TestFailure extends Object { + protected Test fFailedTest; + protected Throwable fThrownException; + + + /** + * Constructs a TestFailure with the given test and exception. + */ + public TestFailure(Test failedTest, Throwable thrownException) { + fFailedTest= failedTest; + fThrownException= thrownException; + } + /** + * Gets the failed test. + */ + public Test failedTest() { + return fFailedTest; + } + /** + * Gets the thrown exception. + */ + public Throwable thrownException() { + return fThrownException; + } + /** + * Returns a short description of the failure. + */ + @Override + public String toString() { + StringBuffer buffer= new StringBuffer(); + buffer.append(fFailedTest+": "+fThrownException.getMessage()); + return buffer.toString(); + } + public String trace() { + StringWriter stringWriter= new StringWriter(); + PrintWriter writer= new PrintWriter(stringWriter); + thrownException().printStackTrace(writer); + StringBuffer buffer= stringWriter.getBuffer(); + return buffer.toString(); + } + public String exceptionMessage() { + return thrownException().getMessage(); + } + public boolean isFailure() { + return thrownException() instanceof AssertionFailedError; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/TestListener.java b/junit4/src/main/java/junit/framework/TestListener.java new file mode 100644 index 0000000..9b69443 --- /dev/null +++ b/junit4/src/main/java/junit/framework/TestListener.java @@ -0,0 +1,23 @@ +package junit.framework; + +/** + * A Listener for test progress + */ +public interface TestListener { + /** + * An error occurred. + */ + public void addError(Test test, Throwable t); + /** + * A failure occurred. + */ + public void addFailure(Test test, AssertionFailedError t); + /** + * A test ended. + */ + public void endTest(Test test); + /** + * A test started. + */ + public void startTest(Test test); +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/TestResult.java b/junit4/src/main/java/junit/framework/TestResult.java new file mode 100644 index 0000000..5768e9a --- /dev/null +++ b/junit4/src/main/java/junit/framework/TestResult.java @@ -0,0 +1,169 @@ +package junit.framework; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +/** + * A <code>TestResult</code> collects the results of executing + * a test case. It is an instance of the Collecting Parameter pattern. + * The test framework distinguishes between <i>failures</i> and <i>errors</i>. + * A failure is anticipated and checked for with assertions. Errors are + * unanticipated problems like an {@link ArrayIndexOutOfBoundsException}. + * + * @see Test + */ +public class TestResult extends Object { + protected List<TestFailure> fFailures; + protected List<TestFailure> fErrors; + protected List<TestListener> fListeners; + protected int fRunTests; + private boolean fStop; + + public TestResult() { + fFailures= new ArrayList<TestFailure>(); + fErrors= new ArrayList<TestFailure>(); + fListeners= new ArrayList<TestListener>(); + fRunTests= 0; + fStop= false; + } + /** + * Adds an error to the list of errors. The passed in exception + * caused the error. + */ + public synchronized void addError(Test test, Throwable t) { + fErrors.add(new TestFailure(test, t)); + for (TestListener each : cloneListeners()) + each.addError(test, t); + } + /** + * Adds a failure to the list of failures. The passed in exception + * caused the failure. + */ + public synchronized void addFailure(Test test, AssertionFailedError t) { + fFailures.add(new TestFailure(test, t)); + for (TestListener each : cloneListeners()) + each.addFailure(test, t); + } + /** + * Registers a TestListener + */ + public synchronized void addListener(TestListener listener) { + fListeners.add(listener); + } + /** + * Unregisters a TestListener + */ + public synchronized void removeListener(TestListener listener) { + fListeners.remove(listener); + } + /** + * Returns a copy of the listeners. + */ + private synchronized List<TestListener> cloneListeners() { + List<TestListener> result= new ArrayList<TestListener>(); + result.addAll(fListeners); + return result; + } + /** + * Informs the result that a test was completed. + */ + public void endTest(Test test) { + for (TestListener each : cloneListeners()) + each.endTest(test); + } + /** + * Gets the number of detected errors. + */ + public synchronized int errorCount() { + return fErrors.size(); + } + /** + * Returns an Enumeration for the errors + */ + public synchronized Enumeration<TestFailure> errors() { + return Collections.enumeration(fErrors); + } + + + /** + * Gets the number of detected failures. + */ + public synchronized int failureCount() { + return fFailures.size(); + } + /** + * Returns an Enumeration for the failures + */ + public synchronized Enumeration<TestFailure> failures() { + return Collections.enumeration(fFailures); + } + + /** + * Runs a TestCase. + */ + protected void run(final TestCase test) { + startTest(test); + Protectable p= new Protectable() { + public void protect() throws Throwable { + test.runBare(); + } + }; + runProtected(test, p); + + endTest(test); + } + /** + * Gets the number of run tests. + */ + public synchronized int runCount() { + return fRunTests; + } + /** + * Runs a TestCase. + */ + public void runProtected(final Test test, Protectable p) { + try { + p.protect(); + } + catch (AssertionFailedError e) { + addFailure(test, e); + } + catch (ThreadDeath e) { // don't catch ThreadDeath by accident + throw e; + } + catch (Throwable e) { + addError(test, e); + } + } + /** + * Checks whether the test run should stop + */ + public synchronized boolean shouldStop() { + return fStop; + } + /** + * Informs the result that a test will be started. + */ + public void startTest(Test test) { + final int count= test.countTestCases(); + synchronized(this) { + fRunTests+= count; + } + for (TestListener each : cloneListeners()) + each.startTest(test); + } + /** + * Marks that the test run should stop. + */ + public synchronized void stop() { + fStop= true; + } + /** + * Returns whether the entire test was successful or not. + */ + public synchronized boolean wasSuccessful() { + return failureCount() == 0 && errorCount() == 0; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/TestSuite.java b/junit4/src/main/java/junit/framework/TestSuite.java new file mode 100644 index 0000000..336efd1 --- /dev/null +++ b/junit4/src/main/java/junit/framework/TestSuite.java @@ -0,0 +1,307 @@ +package junit.framework; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Vector; + +/** + * <p>A <code>TestSuite</code> is a <code>Composite</code> of Tests. + * It runs a collection of test cases. Here is an example using + * the dynamic test definition. + * <pre> + * TestSuite suite= new TestSuite(); + * suite.addTest(new MathTest("testAdd")); + * suite.addTest(new MathTest("testDivideByZero")); + * </pre> + * </p> + * + * <p>Alternatively, a TestSuite can extract the tests to be run automatically. + * To do so you pass the class of your TestCase class to the + * TestSuite constructor. + * <pre> + * TestSuite suite= new TestSuite(MathTest.class); + * </pre> + * </p> + * + * <p>This constructor creates a suite with all the methods + * starting with "test" that take no arguments.</p> + * + * <p>A final option is to do the same for a large array of test classes. + * <pre> + * Class[] testClasses = { MathTest.class, AnotherTest.class } + * TestSuite suite= new TestSuite(testClasses); + * </pre> + * </p> + * + * @see Test + */ +public class TestSuite implements Test { + + /** + * ...as the moon sets over the early morning Merlin, Oregon + * mountains, our intrepid adventurers type... + */ + static public Test createTest(Class<?> theClass, String name) { + Constructor<?> constructor; + try { + constructor= getTestConstructor(theClass); + } catch (NoSuchMethodException e) { + return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"); + } + Object test; + try { + if (constructor.getParameterTypes().length == 0) { + test= constructor.newInstance(new Object[0]); + if (test instanceof TestCase) + ((TestCase) test).setName(name); + } else { + test= constructor.newInstance(new Object[]{name}); + } + } catch (InstantiationException e) { + return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")")); + } catch (InvocationTargetException e) { + return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")")); + } catch (IllegalAccessException e) { + return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")")); + } + return (Test) test; + } + + /** + * Gets a constructor which takes a single String as + * its argument or a no arg constructor. + */ + public static Constructor<?> getTestConstructor(Class<?> theClass) throws NoSuchMethodException { + try { + return theClass.getConstructor(String.class); + } catch (NoSuchMethodException e) { + // fall through + } + return theClass.getConstructor(new Class[0]); + } + + /** + * Returns a test which will fail and log a warning message. + */ + public static Test warning(final String message) { + return new TestCase("warning") { + @Override + protected void runTest() { + fail(message); + } + }; + } + + /** + * Converts the stack trace into a string + */ + private static String exceptionToString(Throwable t) { + StringWriter stringWriter= new StringWriter(); + PrintWriter writer= new PrintWriter(stringWriter); + t.printStackTrace(writer); + return stringWriter.toString(); + } + + private String fName; + + private Vector<Test> fTests= new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners + + /** + * Constructs an empty TestSuite. + */ + public TestSuite() { + } + + /** + * Constructs a TestSuite from the given class. Adds all the methods + * starting with "test" as test cases to the suite. + * Parts of this method were written at 2337 meters in the Hueffihuette, + * Kanton Uri + */ + public TestSuite(final Class<?> theClass) { + addTestsFromTestCase(theClass); + } + + private void addTestsFromTestCase(final Class<?> theClass) { + fName= theClass.getName(); + try { + getTestConstructor(theClass); // Avoid generating multiple error messages + } catch (NoSuchMethodException e) { + addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()")); + return; + } + + if (!Modifier.isPublic(theClass.getModifiers())) { + addTest(warning("Class "+theClass.getName()+" is not public")); + return; + } + + Class<?> superClass= theClass; + List<String> names= new ArrayList<String>(); + while (Test.class.isAssignableFrom(superClass)) { + for (Method each : superClass.getDeclaredMethods()) + addTestMethod(each, names, theClass); + superClass= superClass.getSuperclass(); + } + if (fTests.size() == 0) + addTest(warning("No tests found in "+theClass.getName())); + } + + /** + * Constructs a TestSuite from the given class with the given name. + * @see TestSuite#TestSuite(Class) + */ + public TestSuite(Class<? extends TestCase> theClass, String name) { + this(theClass); + setName(name); + } + + /** + * Constructs an empty TestSuite. + */ + public TestSuite(String name) { + setName(name); + } + + /** + * Constructs a TestSuite from the given array of classes. + * @param classes {@link TestCase}s + */ + public TestSuite (Class<?>... classes) { + for (Class<?> each : classes) + addTest(testCaseForClass(each)); + } + + private Test testCaseForClass(Class<?> each) { + if (TestCase.class.isAssignableFrom(each)) + return new TestSuite(each.asSubclass(TestCase.class)); + else + return warning(each.getCanonicalName() + " does not extend TestCase"); + } + + /** + * Constructs a TestSuite from the given array of classes with the given name. + * @see TestSuite#TestSuite(Class[]) + */ + public TestSuite(Class<? extends TestCase>[] classes, String name) { + this(classes); + setName(name); + } + + /** + * Adds a test to the suite. + */ + public void addTest(Test test) { + fTests.add(test); + } + + /** + * Adds the tests from the given class to the suite + */ + public void addTestSuite(Class<? extends TestCase> testClass) { + addTest(new TestSuite(testClass)); + } + + /** + * Counts the number of test cases that will be run by this test. + */ + public int countTestCases() { + int count= 0; + for (Test each : fTests) + count+= each.countTestCases(); + return count; + } + + /** + * Returns the name of the suite. Not all + * test suites have a name and this method + * can return null. + */ + public String getName() { + return fName; + } + + /** + * Runs the tests and collects their result in a TestResult. + */ + public void run(TestResult result) { + for (Test each : fTests) { + if (result.shouldStop() ) + break; + runTest(each, result); + } + } + + public void runTest(Test test, TestResult result) { + test.run(result); + } + + /** + * Sets the name of the suite. + * @param name the name to set + */ + public void setName(String name) { + fName= name; + } + + /** + * Returns the test at the given index + */ + public Test testAt(int index) { + return fTests.get(index); + } + + /** + * Returns the number of tests in this suite + */ + public int testCount() { + return fTests.size(); + } + + /** + * Returns the tests as an enumeration + */ + public Enumeration<Test> tests() { + return fTests.elements(); + } + + /** + */ + @Override + public String toString() { + if (getName() != null) + return getName(); + return super.toString(); + } + + private void addTestMethod(Method m, List<String> names, Class<?> theClass) { + String name= m.getName(); + if (names.contains(name)) + return; + if (! isPublicTestMethod(m)) { + if (isTestMethod(m)) + addTest(warning("Test method isn't public: "+ m.getName() + "(" + theClass.getCanonicalName() + ")")); + return; + } + names.add(name); + addTest(createTest(theClass, name)); + } + + private boolean isPublicTestMethod(Method m) { + return isTestMethod(m) && Modifier.isPublic(m.getModifiers()); + } + + private boolean isTestMethod(Method m) { + return + m.getParameterTypes().length == 0 && + m.getName().startsWith("test") && + m.getReturnType().equals(Void.TYPE); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/framework/package-info.java b/junit4/src/main/java/junit/framework/package-info.java new file mode 100644 index 0000000..153a1c8 --- /dev/null +++ b/junit4/src/main/java/junit/framework/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides JUnit v3.x core classes. + */ +package junit.framework;
\ No newline at end of file diff --git a/junit4/src/main/java/junit/runner/BaseTestRunner.java b/junit4/src/main/java/junit/runner/BaseTestRunner.java new file mode 100644 index 0000000..6a4b090 --- /dev/null +++ b/junit4/src/main/java/junit/runner/BaseTestRunner.java @@ -0,0 +1,318 @@ +package junit.runner; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.text.NumberFormat; +import java.util.Properties; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestListener; +import junit.framework.TestSuite; + +/** + * Base class for all test runners. + * This class was born live on stage in Sardinia during XP2000. + */ +public abstract class BaseTestRunner implements TestListener { + public static final String SUITE_METHODNAME= "suite"; + + private static Properties fPreferences; + static int fgMaxMessageLength= 500; + static boolean fgFilterStack= true; + boolean fLoading= true; + + /* + * Implementation of TestListener + */ + public synchronized void startTest(Test test) { + testStarted(test.toString()); + } + + protected static void setPreferences(Properties preferences) { + fPreferences= preferences; + } + + protected static Properties getPreferences() { + if (fPreferences == null) { + fPreferences= new Properties(); + fPreferences.put("loading", "true"); + fPreferences.put("filterstack", "true"); + readPreferences(); + } + return fPreferences; + } + + public static void savePreferences() throws IOException { + FileOutputStream fos= new FileOutputStream(getPreferencesFile()); + try { + getPreferences().store(fos, ""); + } finally { + fos.close(); + } + } + + public static void setPreference(String key, String value) { + getPreferences().put(key, value); + } + + public synchronized void endTest(Test test) { + testEnded(test.toString()); + } + + public synchronized void addError(final Test test, final Throwable t) { + testFailed(TestRunListener.STATUS_ERROR, test, t); + } + + public synchronized void addFailure(final Test test, final AssertionFailedError t) { + testFailed(TestRunListener.STATUS_FAILURE, test, t); + } + + // TestRunListener implementation + + public abstract void testStarted(String testName); + + public abstract void testEnded(String testName); + + public abstract void testFailed(int status, Test test, Throwable t); + + /** + * Returns the Test corresponding to the given suite. This is + * a template method, subclasses override runFailed(), clearStatus(). + */ + public Test getTest(String suiteClassName) { + if (suiteClassName.length() <= 0) { + clearStatus(); + return null; + } + Class<?> testClass= null; + try { + testClass= loadSuiteClass(suiteClassName); + } catch (ClassNotFoundException e) { + String clazz= e.getMessage(); + if (clazz == null) + clazz= suiteClassName; + runFailed("Class not found \""+clazz+"\""); + return null; + } catch(Exception e) { + runFailed("Error: "+e.toString()); + return null; + } + Method suiteMethod= null; + try { + suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]); + } catch(Exception e) { + // try to extract a test suite automatically + clearStatus(); + return new TestSuite(testClass); + } + if (! Modifier.isStatic(suiteMethod.getModifiers())) { + runFailed("Suite() method must be static"); + return null; + } + Test test= null; + try { + test= (Test)suiteMethod.invoke(null, (Object[])new Class[0]); // static method + if (test == null) + return test; + } + catch (InvocationTargetException e) { + runFailed("Failed to invoke suite():" + e.getTargetException().toString()); + return null; + } + catch (IllegalAccessException e) { + runFailed("Failed to invoke suite():" + e.toString()); + return null; + } + + clearStatus(); + return test; + } + + /** + * Returns the formatted string of the elapsed time. + */ + public String elapsedTimeAsString(long runTime) { + return NumberFormat.getInstance().format((double)runTime/1000); + } + + /** + * Processes the command line arguments and + * returns the name of the suite class to run or null + */ + protected String processArguments(String[] args) { + String suiteName= null; + for (int i= 0; i < args.length; i++) { + if (args[i].equals("-noloading")) { + setLoading(false); + } else if (args[i].equals("-nofilterstack")) { + fgFilterStack= false; + } else if (args[i].equals("-c")) { + if (args.length > i+1) + suiteName= extractClassName(args[i+1]); + else + System.out.println("Missing Test class name"); + i++; + } else { + suiteName= args[i]; + } + } + return suiteName; + } + + /** + * Sets the loading behaviour of the test runner + */ + public void setLoading(boolean enable) { + fLoading= enable; + } + /** + * Extract the class name from a String in VA/Java style + */ + public String extractClassName(String className) { + if(className.startsWith("Default package for")) + return className.substring(className.lastIndexOf(".")+1); + return className; + } + + /** + * Truncates a String to the maximum length. + */ + public static String truncate(String s) { + if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength) + s= s.substring(0, fgMaxMessageLength)+"..."; + return s; + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + */ + protected abstract void runFailed(String message); + + /** + * Returns the loaded Class for a suite name. + */ + protected Class<?> loadSuiteClass(String suiteClassName) throws ClassNotFoundException { + return Class.forName(suiteClassName); + } + + /** + * Clears the status message. + */ + protected void clearStatus() { // Belongs in the GUI TestRunner class + } + + protected boolean useReloadingTestSuiteLoader() { + return getPreference("loading").equals("true") && fLoading; + } + + private static File getPreferencesFile() { + String home= System.getProperty("user.home"); + return new File(home, "junit.properties"); + } + + private static void readPreferences() { + InputStream is= null; + try { + is= new FileInputStream(getPreferencesFile()); + setPreferences(new Properties(getPreferences())); + getPreferences().load(is); + } catch (IOException e) { + try { + if (is != null) + is.close(); + } catch (IOException e1) { + } + } + } + + public static String getPreference(String key) { + return getPreferences().getProperty(key); + } + + public static int getPreference(String key, int dflt) { + String value= getPreference(key); + int intValue= dflt; + if (value == null) + return intValue; + try { + intValue= Integer.parseInt(value); + } catch (NumberFormatException ne) { + } + return intValue; + } + + /** + * Returns a filtered stack trace + */ + public static String getFilteredTrace(Throwable t) { + StringWriter stringWriter= new StringWriter(); + PrintWriter writer= new PrintWriter(stringWriter); + t.printStackTrace(writer); + StringBuffer buffer= stringWriter.getBuffer(); + String trace= buffer.toString(); + return BaseTestRunner.getFilteredTrace(trace); + } + + /** + * Filters stack frames from internal JUnit classes + */ + public static String getFilteredTrace(String stack) { + if (showStackRaw()) + return stack; + + StringWriter sw= new StringWriter(); + PrintWriter pw= new PrintWriter(sw); + StringReader sr= new StringReader(stack); + BufferedReader br= new BufferedReader(sr); + + String line; + try { + while ((line= br.readLine()) != null) { + if (!filterLine(line)) + pw.println(line); + } + } catch (Exception IOException) { + return stack; // return the stack unfiltered + } + return sw.toString(); + } + + protected static boolean showStackRaw() { + return !getPreference("filterstack").equals("true") || fgFilterStack == false; + } + + static boolean filterLine(String line) { + String[] patterns= new String[] { + "junit.framework.TestCase", + "junit.framework.TestResult", + "junit.framework.TestSuite", + "junit.framework.Assert.", // don't filter AssertionFailure + "junit.swingui.TestRunner", + "junit.awtui.TestRunner", + "junit.textui.TestRunner", + "java.lang.reflect.Method.invoke(" + }; + for (int i= 0; i < patterns.length; i++) { + if (line.indexOf(patterns[i]) > 0) + return true; + } + return false; + } + + static { + fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength); + } + +} diff --git a/junit4/src/main/java/junit/runner/TestRunListener.java b/junit4/src/main/java/junit/runner/TestRunListener.java new file mode 100644 index 0000000..b11ef07 --- /dev/null +++ b/junit4/src/main/java/junit/runner/TestRunListener.java @@ -0,0 +1,19 @@ +package junit.runner; +/** + * A listener interface for observing the + * execution of a test run. Unlike TestListener, + * this interface using only primitive objects, + * making it suitable for remote test execution. + */ + public interface TestRunListener { + /* test status constants*/ + public static final int STATUS_ERROR= 1; + public static final int STATUS_FAILURE= 2; + + public void testRunStarted(String testSuiteName, int testCount); + public void testRunEnded(long elapsedTime); + public void testRunStopped(long elapsedTime); + public void testStarted(String testName); + public void testEnded(String testName); + public void testFailed(int status, String testName, String trace); +} diff --git a/junit4/src/main/java/junit/runner/Version.java b/junit4/src/main/java/junit/runner/Version.java new file mode 100644 index 0000000..21aabfa --- /dev/null +++ b/junit4/src/main/java/junit/runner/Version.java @@ -0,0 +1,18 @@ +package junit.runner; + +/** + * This class defines the current version of JUnit + */ +public class Version { + private Version() { + // don't instantiate + } + + public static String id() { + return "4.10-SNAPSHOT"; + } + + public static void main(String[] args) { + System.out.println(id()); + } +} diff --git a/junit4/src/main/java/junit/runner/Version.java.template b/junit4/src/main/java/junit/runner/Version.java.template new file mode 100644 index 0000000..3182cfd --- /dev/null +++ b/junit4/src/main/java/junit/runner/Version.java.template @@ -0,0 +1,18 @@ +package junit.runner; + +/** + * This class defines the current version of JUnit + */ +public class Version { + private Version() { + // don't instantiate + } + + public static String id() { + return "@version@"; + } + + public static void main(String[] args) { + System.out.println(id()); + } +} diff --git a/junit4/src/main/java/junit/runner/logo.gif b/junit4/src/main/java/junit/runner/logo.gif Binary files differnew file mode 100644 index 0000000..d0e1547 --- /dev/null +++ b/junit4/src/main/java/junit/runner/logo.gif diff --git a/junit4/src/main/java/junit/runner/package-info.java b/junit4/src/main/java/junit/runner/package-info.java new file mode 100644 index 0000000..b746185 --- /dev/null +++ b/junit4/src/main/java/junit/runner/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides JUnit v3.x test runners. + */ +package junit.runner;
\ No newline at end of file diff --git a/junit4/src/main/java/junit/runner/smalllogo.gif b/junit4/src/main/java/junit/runner/smalllogo.gif Binary files differnew file mode 100644 index 0000000..7b25eaf --- /dev/null +++ b/junit4/src/main/java/junit/runner/smalllogo.gif diff --git a/junit4/src/main/java/junit/textui/ResultPrinter.java b/junit4/src/main/java/junit/textui/ResultPrinter.java new file mode 100644 index 0000000..f2f01f5 --- /dev/null +++ b/junit4/src/main/java/junit/textui/ResultPrinter.java @@ -0,0 +1,139 @@ + +package junit.textui; + +import java.io.PrintStream; +import java.text.NumberFormat; +import java.util.Enumeration; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestFailure; +import junit.framework.TestListener; +import junit.framework.TestResult; +import junit.runner.BaseTestRunner; + +public class ResultPrinter implements TestListener { + PrintStream fWriter; + int fColumn= 0; + + public ResultPrinter(PrintStream writer) { + fWriter= writer; + } + + /* API for use by textui.TestRunner + */ + + synchronized void print(TestResult result, long runTime) { + printHeader(runTime); + printErrors(result); + printFailures(result); + printFooter(result); + } + + void printWaitPrompt() { + getWriter().println(); + getWriter().println("<RETURN> to continue"); + } + + /* Internal methods + */ + + protected void printHeader(long runTime) { + getWriter().println(); + getWriter().println("Time: "+elapsedTimeAsString(runTime)); + } + + protected void printErrors(TestResult result) { + printDefects(result.errors(), result.errorCount(), "error"); + } + + protected void printFailures(TestResult result) { + printDefects(result.failures(), result.failureCount(), "failure"); + } + + protected void printDefects(Enumeration<TestFailure> booBoos, int count, String type) { + if (count == 0) return; + if (count == 1) + getWriter().println("There was " + count + " " + type + ":"); + else + getWriter().println("There were " + count + " " + type + "s:"); + for (int i= 1; booBoos.hasMoreElements(); i++) { + printDefect(booBoos.nextElement(), i); + } + } + + public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes + printDefectHeader(booBoo, count); + printDefectTrace(booBoo); + } + + protected void printDefectHeader(TestFailure booBoo, int count) { + // I feel like making this a println, then adding a line giving the throwable a chance to print something + // before we get to the stack trace. + getWriter().print(count + ") " + booBoo.failedTest()); + } + + protected void printDefectTrace(TestFailure booBoo) { + getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace())); + } + + protected void printFooter(TestResult result) { + if (result.wasSuccessful()) { + getWriter().println(); + getWriter().print("OK"); + getWriter().println (" (" + result.runCount() + " test" + (result.runCount() == 1 ? "": "s") + ")"); + + } else { + getWriter().println(); + getWriter().println("FAILURES!!!"); + getWriter().println("Tests run: "+result.runCount()+ + ", Failures: "+result.failureCount()+ + ", Errors: "+result.errorCount()); + } + getWriter().println(); + } + + + /** + * Returns the formatted string of the elapsed time. + * Duplicated from BaseTestRunner. Fix it. + */ + protected String elapsedTimeAsString(long runTime) { + return NumberFormat.getInstance().format((double)runTime/1000); + } + + public PrintStream getWriter() { + return fWriter; + } + /** + * @see junit.framework.TestListener#addError(Test, Throwable) + */ + public void addError(Test test, Throwable t) { + getWriter().print("E"); + } + + /** + * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError) + */ + public void addFailure(Test test, AssertionFailedError t) { + getWriter().print("F"); + } + + /** + * @see junit.framework.TestListener#endTest(Test) + */ + public void endTest(Test test) { + } + + /** + * @see junit.framework.TestListener#startTest(Test) + */ + public void startTest(Test test) { + getWriter().print("."); + if (fColumn++ >= 40) { + getWriter().println(); + fColumn= 0; + } + } + +} diff --git a/junit4/src/main/java/junit/textui/TestRunner.java b/junit4/src/main/java/junit/textui/TestRunner.java new file mode 100644 index 0000000..046448e --- /dev/null +++ b/junit4/src/main/java/junit/textui/TestRunner.java @@ -0,0 +1,203 @@ +package junit.textui; + + +import java.io.PrintStream; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import junit.runner.BaseTestRunner; +import junit.runner.Version; + +/** + * A command line based tool to run tests. + * <pre> + * java junit.textui.TestRunner [-wait] TestCaseClass + * </pre> + * + * <p>TestRunner expects the name of a TestCase class as argument. + * If this class defines a static <code>suite</code> method it + * will be invoked and the returned test is run. Otherwise all + * the methods starting with "test" having no arguments are run.</p> + * + * <p> When the wait command line argument is given TestRunner + * waits until the users types RETURN.</p> + * + * <p>TestRunner prints a trace as the tests are executed followed by a + * summary at the end.</p> + */ +public class TestRunner extends BaseTestRunner { + private ResultPrinter fPrinter; + + public static final int SUCCESS_EXIT= 0; + public static final int FAILURE_EXIT= 1; + public static final int EXCEPTION_EXIT= 2; + + /** + * Constructs a TestRunner. + */ + public TestRunner() { + this(System.out); + } + + /** + * Constructs a TestRunner using the given stream for all the output + */ + public TestRunner(PrintStream writer) { + this(new ResultPrinter(writer)); + } + + /** + * Constructs a TestRunner using the given ResultPrinter all the output + */ + public TestRunner(ResultPrinter printer) { + fPrinter= printer; + } + + /** + * Runs a suite extracted from a TestCase subclass. + */ + static public void run(Class<? extends TestCase> testClass) { + run(new TestSuite(testClass)); + } + + /** + * Runs a single test and collects its results. + * This method can be used to start a test run + * from your program. + * <pre> + * public static void main (String[] args) { + * test.textui.TestRunner.run(suite()); + * } + * </pre> + */ + static public TestResult run(Test test) { + TestRunner runner= new TestRunner(); + return runner.doRun(test); + } + + /** + * Runs a single test and waits until the user + * types RETURN. + */ + static public void runAndWait(Test suite) { + TestRunner aTestRunner= new TestRunner(); + aTestRunner.doRun(suite, true); + } + + @Override + public void testFailed(int status, Test test, Throwable t) { + } + + @Override + public void testStarted(String testName) { + } + + @Override + public void testEnded(String testName) { + } + + /** + * Creates the TestResult to be used for the test run. + */ + protected TestResult createTestResult() { + return new TestResult(); + } + + public TestResult doRun(Test test) { + return doRun(test, false); + } + + public TestResult doRun(Test suite, boolean wait) { + TestResult result= createTestResult(); + result.addListener(fPrinter); + long startTime= System.currentTimeMillis(); + suite.run(result); + long endTime= System.currentTimeMillis(); + long runTime= endTime-startTime; + fPrinter.print(result, runTime); + + pause(wait); + return result; + } + + protected void pause(boolean wait) { + if (!wait) return; + fPrinter.printWaitPrompt(); + try { + System.in.read(); + } + catch(Exception e) { + } + } + + public static void main(String args[]) { + TestRunner aTestRunner= new TestRunner(); + try { + TestResult r= aTestRunner.start(args); + if (!r.wasSuccessful()) + System.exit(FAILURE_EXIT); + System.exit(SUCCESS_EXIT); + } catch(Exception e) { + System.err.println(e.getMessage()); + System.exit(EXCEPTION_EXIT); + } + } + + /** + * Starts a test run. Analyzes the command line arguments and runs the given + * test suite. + */ + public TestResult start(String args[]) throws Exception { + String testCase= ""; + String method= ""; + boolean wait= false; + + for (int i= 0; i < args.length; i++) { + if (args[i].equals("-wait")) + wait= true; + else if (args[i].equals("-c")) + testCase= extractClassName(args[++i]); + else if (args[i].equals("-m")) { + String arg= args[++i]; + int lastIndex= arg.lastIndexOf('.'); + testCase= arg.substring(0, lastIndex); + method= arg.substring(lastIndex + 1); + } else if (args[i].equals("-v")) + System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma"); + else + testCase= args[i]; + } + + if (testCase.equals("")) + throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class"); + + try { + if (!method.equals("")) + return runSingleMethod(testCase, method, wait); + Test suite= getTest(testCase); + return doRun(suite, wait); + } catch (Exception e) { + throw new Exception("Could not create and run test suite: " + e); + } + } + + protected TestResult runSingleMethod(String testCase, String method, boolean wait) throws Exception { + Class<? extends TestCase> testClass= loadSuiteClass(testCase).asSubclass(TestCase.class); + Test test= TestSuite.createTest(testClass, method); + return doRun(test, wait); + } + + @Override + protected void runFailed(String message) { + System.err.println(message); + System.exit(FAILURE_EXIT); + } + + public void setPrinter(ResultPrinter printer) { + fPrinter= printer; + } + + +}
\ No newline at end of file diff --git a/junit4/src/main/java/junit/textui/package-info.java b/junit4/src/main/java/junit/textui/package-info.java new file mode 100644 index 0000000..2aa5176 --- /dev/null +++ b/junit4/src/main/java/junit/textui/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides JUnit v3.x command line based tool to run tests. + */ +package junit.textui;
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/After.java b/junit4/src/main/java/org/junit/After.java new file mode 100644 index 0000000..39aa6e5 --- /dev/null +++ b/junit4/src/main/java/org/junit/After.java @@ -0,0 +1,40 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>If you allocate external resources in a {@link org.junit.Before} method you need to release them + * after the test runs. Annotating a <code>public void</code> method + * with <code>@After</code> causes that method to be run after the {@link org.junit.Test} method. All <code>@After</code> + * methods are guaranteed to run even if a {@link org.junit.Before} or {@link org.junit.Test} method throws an + * exception. The <code>@After</code> methods declared in superclasses will be run after those of the current + * class.</p> + * + * Here is a simple example: +* <pre> + * public class Example { + * File output; + * @Before public void createOutputFile() { + * output= new File(...); + * } + * @Test public void something() { + * ... + * } + * @After public void deleteOutputFile() { + * output.delete(); + * } + * } + * </pre> + * + * @see org.junit.Before + * @see org.junit.Test + */ + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface After { +} + diff --git a/junit4/src/main/java/org/junit/AfterClass.java b/junit4/src/main/java/org/junit/AfterClass.java new file mode 100644 index 0000000..2d6bc80 --- /dev/null +++ b/junit4/src/main/java/org/junit/AfterClass.java @@ -0,0 +1,41 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>If you allocate expensive external resources in a {@link org.junit.BeforeClass} method you need to release them + * after all the tests in the class have run. Annotating a <code>public static void</code> method + * with <code>@AfterClass</code> causes that method to be run after all the tests in the class have been run. All <code>@AfterClass</code> + * methods are guaranteed to run even if a {@link org.junit.BeforeClass} method throws an + * exception. The <code>@AfterClass</code> methods declared in superclasses will be run after those of the current + * class.</p> + * + * Here is a simple example: +* <pre> + * public class Example { + * private static DatabaseConnection database; + * @BeforeClass public static void login() { + * database= ...; + * } + * @Test public void something() { + * ... + * } + * @Test public void somethingElse() { + * ... + * } + * @AfterClass public static void logout() { + * database.logout(); + * } + * } + * </pre> + * + * @see org.junit.BeforeClass + * @see org.junit.Test + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AfterClass { +} diff --git a/junit4/src/main/java/org/junit/Assert.java b/junit4/src/main/java/org/junit/Assert.java new file mode 100644 index 0000000..b585b87 --- /dev/null +++ b/junit4/src/main/java/org/junit/Assert.java @@ -0,0 +1,783 @@ +package org.junit; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.junit.internal.ArrayComparisonFailure; +import org.junit.internal.ExactComparisonCriteria; +import org.junit.internal.InexactComparisonCriteria; + +/** + * A set of assertion methods useful for writing tests. Only failed assertions + * are recorded. These methods can be used directly: + * <code>Assert.assertEquals(...)</code>, however, they read better if they + * are referenced through static import:<br/> + * + * <pre> + * import static org.junit.Assert.*; + * ... + * assertEquals(...); + * </pre> + * + * @see AssertionError + */ +public class Assert { + /** + * Protect constructor since it is a static only class + */ + protected Assert() { + } + + /** + * Asserts that a condition is true. If it isn't it throws an + * {@link AssertionError} with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param condition + * condition to be checked + */ + static public void assertTrue(String message, boolean condition) { + if (!condition) + fail(message); + } + + /** + * Asserts that a condition is true. If it isn't it throws an + * {@link AssertionError} without a message. + * + * @param condition + * condition to be checked + */ + static public void assertTrue(boolean condition) { + assertTrue(null, condition); + } + + /** + * Asserts that a condition is false. If it isn't it throws an + * {@link AssertionError} with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param condition + * condition to be checked + */ + static public void assertFalse(String message, boolean condition) { + assertTrue(message, !condition); + } + + /** + * Asserts that a condition is false. If it isn't it throws an + * {@link AssertionError} without a message. + * + * @param condition + * condition to be checked + */ + static public void assertFalse(boolean condition) { + assertFalse(null, condition); + } + + /** + * Fails a test with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @see AssertionError + */ + static public void fail(String message) { + if (message == null) + throw new AssertionError(); + throw new AssertionError(message); + } + + /** + * Fails a test with no message. + */ + static public void fail() { + fail(null); + } + + /** + * Asserts that two objects are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. If + * <code>expected</code> and <code>actual</code> are <code>null</code>, + * they are considered equal. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expected + * expected value + * @param actual + * actual value + */ + static public void assertEquals(String message, Object expected, + Object actual) { + if (expected == null && actual == null) + return; + if (expected != null && isEquals(expected, actual)) + return; + else if (expected instanceof String && actual instanceof String) { + String cleanMessage= message == null ? "" : message; + throw new ComparisonFailure(cleanMessage, (String) expected, + (String) actual); + } else + failNotEquals(message, expected, actual); + } + + private static boolean isEquals(Object expected, Object actual) { + return expected.equals(actual); + } + + /** + * Asserts that two objects are equal. If they are not, an + * {@link AssertionError} without a message is thrown. If + * <code>expected</code> and <code>actual</code> are <code>null</code>, + * they are considered equal. + * + * @param expected + * expected value + * @param actual + * the value to check against <code>expected</code> + */ + static public void assertEquals(Object expected, Object actual) { + assertEquals(null, expected, actual); + } + + /** + * Asserts that two object arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. If + * <code>expecteds</code> and <code>actuals</code> are <code>null</code>, + * they are considered equal. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * Object array or array of arrays (multi-dimensional array) with + * expected values. + * @param actuals + * Object array or array of arrays (multi-dimensional array) with + * actual values + */ + public static void assertArrayEquals(String message, Object[] expecteds, + Object[] actuals) throws ArrayComparisonFailure { + internalArrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two object arrays are equal. If they are not, an + * {@link AssertionError} is thrown. If <code>expected</code> and + * <code>actual</code> are <code>null</code>, they are considered + * equal. + * + * @param expecteds + * Object array or array of arrays (multi-dimensional array) with + * expected values + * @param actuals + * Object array or array of arrays (multi-dimensional array) with + * actual values + */ + public static void assertArrayEquals(Object[] expecteds, Object[] actuals) { + assertArrayEquals(null, expecteds, actuals); + } + + /** + * Asserts that two byte arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * byte array with expected values. + * @param actuals + * byte array with actual values + */ + public static void assertArrayEquals(String message, byte[] expecteds, + byte[] actuals) throws ArrayComparisonFailure { + internalArrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two byte arrays are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expecteds + * byte array with expected values. + * @param actuals + * byte array with actual values + */ + public static void assertArrayEquals(byte[] expecteds, byte[] actuals) { + assertArrayEquals(null, expecteds, actuals); + } + + /** + * Asserts that two char arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * char array with expected values. + * @param actuals + * char array with actual values + */ + public static void assertArrayEquals(String message, char[] expecteds, + char[] actuals) throws ArrayComparisonFailure { + internalArrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two char arrays are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expecteds + * char array with expected values. + * @param actuals + * char array with actual values + */ + public static void assertArrayEquals(char[] expecteds, char[] actuals) { + assertArrayEquals(null, expecteds, actuals); + } + + /** + * Asserts that two short arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * short array with expected values. + * @param actuals + * short array with actual values + */ + public static void assertArrayEquals(String message, short[] expecteds, + short[] actuals) throws ArrayComparisonFailure { + internalArrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two short arrays are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expecteds + * short array with expected values. + * @param actuals + * short array with actual values + */ + public static void assertArrayEquals(short[] expecteds, short[] actuals) { + assertArrayEquals(null, expecteds, actuals); + } + + /** + * Asserts that two int arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * int array with expected values. + * @param actuals + * int array with actual values + */ + public static void assertArrayEquals(String message, int[] expecteds, + int[] actuals) throws ArrayComparisonFailure { + internalArrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two int arrays are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expecteds + * int array with expected values. + * @param actuals + * int array with actual values + */ + public static void assertArrayEquals(int[] expecteds, int[] actuals) { + assertArrayEquals(null, expecteds, actuals); + } + + /** + * Asserts that two long arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * long array with expected values. + * @param actuals + * long array with actual values + */ + public static void assertArrayEquals(String message, long[] expecteds, + long[] actuals) throws ArrayComparisonFailure { + internalArrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two long arrays are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expecteds + * long array with expected values. + * @param actuals + * long array with actual values + */ + public static void assertArrayEquals(long[] expecteds, long[] actuals) { + assertArrayEquals(null, expecteds, actuals); + } + + /** + * Asserts that two double arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * double array with expected values. + * @param actuals + * double array with actual values + */ + public static void assertArrayEquals(String message, double[] expecteds, + double[] actuals, double delta) throws ArrayComparisonFailure { + new InexactComparisonCriteria(delta).arrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two double arrays are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expecteds + * double array with expected values. + * @param actuals + * double array with actual values + */ + public static void assertArrayEquals(double[] expecteds, double[] actuals, double delta) { + assertArrayEquals(null, expecteds, actuals, delta); + } + + /** + * Asserts that two float arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * float array with expected values. + * @param actuals + * float array with actual values + */ + public static void assertArrayEquals(String message, float[] expecteds, + float[] actuals, float delta) throws ArrayComparisonFailure { + new InexactComparisonCriteria(delta).arrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two float arrays are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expecteds + * float array with expected values. + * @param actuals + * float array with actual values + */ + public static void assertArrayEquals(float[] expecteds, float[] actuals, float delta) { + assertArrayEquals(null, expecteds, actuals, delta); + } + + /** + * Asserts that two object arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. If + * <code>expecteds</code> and <code>actuals</code> are <code>null</code>, + * they are considered equal. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * Object array or array of arrays (multi-dimensional array) with + * expected values. + * @param actuals + * Object array or array of arrays (multi-dimensional array) with + * actual values + */ + private static void internalArrayEquals(String message, Object expecteds, + Object actuals) throws ArrayComparisonFailure { + new ExactComparisonCriteria().arrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two doubles or floats are equal to within a positive delta. + * If they are not, an {@link AssertionError} is thrown with the given + * message. If the expected value is infinity then the delta value is + * ignored. NaNs are considered equal: + * <code>assertEquals(Double.NaN, Double.NaN, *)</code> passes + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expected + * expected value + * @param actual + * the value to check against <code>expected</code> + * @param delta + * the maximum delta between <code>expected</code> and + * <code>actual</code> for which both numbers are still + * considered equal. + */ + static public void assertEquals(String message, double expected, + double actual, double delta) { + if (Double.compare(expected, actual) == 0) + return; + if (!(Math.abs(expected - actual) <= delta)) + failNotEquals(message, new Double(expected), new Double(actual)); + } + + /** + * Asserts that two longs are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expected + * expected long value. + * @param actual + * actual long value + */ + static public void assertEquals(long expected, long actual) { + assertEquals(null, expected, actual); + } + + /** + * Asserts that two longs are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expected + * long expected value. + * @param actual + * long actual value + */ + static public void assertEquals(String message, long expected, long actual) { + assertEquals(message, (Long) expected, (Long) actual); + } + + /** + * @deprecated Use + * <code>assertEquals(double expected, double actual, double delta)</code> + * instead + */ + @Deprecated + static public void assertEquals(double expected, double actual) { + assertEquals(null, expected, actual); + } + + /** + * @deprecated Use + * <code>assertEquals(String message, double expected, double actual, double delta)</code> + * instead + */ + @Deprecated + static public void assertEquals(String message, double expected, + double actual) { + fail("Use assertEquals(expected, actual, delta) to compare floating-point numbers"); + } + + /** + * Asserts that two doubles or floats are equal to within a positive delta. + * If they are not, an {@link AssertionError} is thrown. If the expected + * value is infinity then the delta value is ignored.NaNs are considered + * equal: <code>assertEquals(Double.NaN, Double.NaN, *)</code> passes + * + * @param expected + * expected value + * @param actual + * the value to check against <code>expected</code> + * @param delta + * the maximum delta between <code>expected</code> and + * <code>actual</code> for which both numbers are still + * considered equal. + */ + static public void assertEquals(double expected, double actual, double delta) { + assertEquals(null, expected, actual, delta); + } + + /** + * Asserts that an object isn't null. If it is an {@link AssertionError} is + * thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param object + * Object to check or <code>null</code> + */ + static public void assertNotNull(String message, Object object) { + assertTrue(message, object != null); + } + + /** + * Asserts that an object isn't null. If it is an {@link AssertionError} is + * thrown. + * + * @param object + * Object to check or <code>null</code> + */ + static public void assertNotNull(Object object) { + assertNotNull(null, object); + } + + /** + * Asserts that an object is null. If it is not, an {@link AssertionError} + * is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param object + * Object to check or <code>null</code> + */ + static public void assertNull(String message, Object object) { + assertTrue(message, object == null); + } + + /** + * Asserts that an object is null. If it isn't an {@link AssertionError} is + * thrown. + * + * @param object + * Object to check or <code>null</code> + */ + static public void assertNull(Object object) { + assertNull(null, object); + } + + /** + * Asserts that two objects refer to the same object. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expected + * the expected object + * @param actual + * the object to compare to <code>expected</code> + */ + static public void assertSame(String message, Object expected, Object actual) { + if (expected == actual) + return; + failNotSame(message, expected, actual); + } + + /** + * Asserts that two objects refer to the same object. If they are not the + * same, an {@link AssertionError} without a message is thrown. + * + * @param expected + * the expected object + * @param actual + * the object to compare to <code>expected</code> + */ + static public void assertSame(Object expected, Object actual) { + assertSame(null, expected, actual); + } + + /** + * Asserts that two objects do not refer to the same object. If they do + * refer to the same object, an {@link AssertionError} is thrown with the + * given message. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param unexpected + * the object you don't expect + * @param actual + * the object to compare to <code>unexpected</code> + */ + static public void assertNotSame(String message, Object unexpected, + Object actual) { + if (unexpected == actual) + failSame(message); + } + + /** + * Asserts that two objects do not refer to the same object. If they do + * refer to the same object, an {@link AssertionError} without a message is + * thrown. + * + * @param unexpected + * the object you don't expect + * @param actual + * the object to compare to <code>unexpected</code> + */ + static public void assertNotSame(Object unexpected, Object actual) { + assertNotSame(null, unexpected, actual); + } + + static private void failSame(String message) { + String formatted= ""; + if (message != null) + formatted= message + " "; + fail(formatted + "expected not same"); + } + + static private void failNotSame(String message, Object expected, + Object actual) { + String formatted= ""; + if (message != null) + formatted= message + " "; + fail(formatted + "expected same:<" + expected + "> was not:<" + actual + + ">"); + } + + static private void failNotEquals(String message, Object expected, + Object actual) { + fail(format(message, expected, actual)); + } + + static String format(String message, Object expected, Object actual) { + String formatted= ""; + if (message != null && !message.equals("")) + formatted= message + " "; + String expectedString= String.valueOf(expected); + String actualString= String.valueOf(actual); + if (expectedString.equals(actualString)) + return formatted + "expected: " + + formatClassAndValue(expected, expectedString) + + " but was: " + formatClassAndValue(actual, actualString); + else + return formatted + "expected:<" + expectedString + "> but was:<" + + actualString + ">"; + } + + private static String formatClassAndValue(Object value, String valueString) { + String className= value == null ? "null" : value.getClass().getName(); + return className + "<" + valueString + ">"; + } + + /** + * Asserts that two object arrays are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. If + * <code>expecteds</code> and <code>actuals</code> are <code>null</code>, + * they are considered equal. + * + * @param message + * the identifying message for the {@link AssertionError} (<code>null</code> + * okay) + * @param expecteds + * Object array or array of arrays (multi-dimensional array) with + * expected values. + * @param actuals + * Object array or array of arrays (multi-dimensional array) with + * actual values + * @deprecated use assertArrayEquals + */ + @Deprecated + public static void assertEquals(String message, Object[] expecteds, + Object[] actuals) { + assertArrayEquals(message, expecteds, actuals); + } + + /** + * Asserts that two object arrays are equal. If they are not, an + * {@link AssertionError} is thrown. If <code>expected</code> and + * <code>actual</code> are <code>null</code>, they are considered + * equal. + * + * @param expecteds + * Object array or array of arrays (multi-dimensional array) with + * expected values + * @param actuals + * Object array or array of arrays (multi-dimensional array) with + * actual values + * @deprecated use assertArrayEquals + */ + @Deprecated + public static void assertEquals(Object[] expecteds, Object[] actuals) { + assertArrayEquals(expecteds, actuals); + } + + /** + * Asserts that <code>actual</code> satisfies the condition specified by + * <code>matcher</code>. If not, an {@link AssertionError} is thrown with + * information about the matcher and failing value. Example: + * + * <pre> + * assertThat(0, is(1)); // fails: + * // failure message: + * // expected: is <1> + * // got value: <0> + * assertThat(0, is(not(1))) // passes + * </pre> + * + * @param <T> + * the static type accepted by the matcher (this can flag obvious + * compile-time problems such as {@code assertThat(1, is("a"))} + * @param actual + * the computed value being compared + * @param matcher + * an expression, built of {@link Matcher}s, specifying allowed + * values + * + * @see org.hamcrest.CoreMatchers + * @see org.junit.matchers.JUnitMatchers + */ + public static <T> void assertThat(T actual, Matcher<T> matcher) { + assertThat("", actual, matcher); + } + + /** + * Asserts that <code>actual</code> satisfies the condition specified by + * <code>matcher</code>. If not, an {@link AssertionError} is thrown with + * the reason and information about the matcher and failing value. Example: + * + * <pre> + * : + * assertThat("Help! Integers don't work", 0, is(1)); // fails: + * // failure message: + * // Help! Integers don't work + * // expected: is <1> + * // got value: <0> + * assertThat("Zero is one", 0, is(not(1))) // passes + * </pre> + * + * @param reason + * additional information about the error + * @param <T> + * the static type accepted by the matcher (this can flag obvious + * compile-time problems such as {@code assertThat(1, is("a"))} + * @param actual + * the computed value being compared + * @param matcher + * an expression, built of {@link Matcher}s, specifying allowed + * values + * + * @see org.hamcrest.CoreMatchers + * @see org.junit.matchers.JUnitMatchers + */ + public static <T> void assertThat(String reason, T actual, + Matcher<T> matcher) { + if (!matcher.matches(actual)) { + Description description= new StringDescription(); + description.appendText(reason); + description.appendText("\nExpected: "); + description.appendDescriptionOf(matcher); + description.appendText("\n got: "); + description.appendValue(actual); + description.appendText("\n"); + throw new java.lang.AssertionError(description.toString()); + } + } +} diff --git a/junit4/src/main/java/org/junit/Assume.java b/junit4/src/main/java/org/junit/Assume.java new file mode 100644 index 0000000..7b6c21a --- /dev/null +++ b/junit4/src/main/java/org/junit/Assume.java @@ -0,0 +1,94 @@ +package org.junit; + +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import org.hamcrest.Matcher; +import org.junit.internal.AssumptionViolatedException; +import org.junit.internal.matchers.Each; + +/** + * A set of methods useful for stating assumptions about the conditions in which a test is meaningful. + * A failed assumption does not mean the code is broken, but that the test provides no useful information. + * The default JUnit runner treats tests with failing assumptions as ignored. Custom runners may behave differently. + * + * For example: + * <pre> + * // only provides information if database is reachable. + * \@Test public void calculateTotalSalary() { + * DBConnection dbc = Database.connect(); + * assumeNotNull(dbc); + * // ... + * } + * </pre> + * These methods can be used directly: <code>Assume.assumeTrue(...)</code>, however, they + * read better if they are referenced through static import:<br/> + * <pre> + * import static org.junit.Assume.*; + * ... + * assumeTrue(...); + * </pre> + */ +public class Assume { + /** + * If called with an expression evaluating to {@code false}, the test will halt and be ignored. + * @param b + */ + public static void assumeTrue(boolean b) { + assumeThat(b, is(true)); + } + + /** + * If called with one or more null elements in <code>objects</code>, the test will halt and be ignored. + * @param objects + */ + public static void assumeNotNull(Object... objects) { + assumeThat(asList(objects), Each.each(notNullValue())); + } + + /** + * Call to assume that <code>actual</code> satisfies the condition specified by <code>matcher</code>. + * If not, the test halts and is ignored. + * Example: + * <pre>: + * assumeThat(1, is(1)); // passes + * foo(); // will execute + * assumeThat(0, is(1)); // assumption failure! test halts + * int x = 1 / 0; // will never execute + * </pre> + * + * @param <T> the static type accepted by the matcher (this can flag obvious compile-time problems such as {@code assumeThat(1, is("a"))} + * @param actual the computed value being compared + * @param matcher an expression, built of {@link Matcher}s, specifying allowed values + * + * @see org.hamcrest.CoreMatchers + * @see org.junit.matchers.JUnitMatchers + */ + public static <T> void assumeThat(T actual, Matcher<T> matcher) { + if (!matcher.matches(actual)) + throw new AssumptionViolatedException(actual, matcher); + } + + /** + * Use to assume that an operation completes normally. If {@code t} is non-null, the test will halt and be ignored. + * + * For example: + * <pre> + * \@Test public void parseDataFile() { + * DataFile file; + * try { + * file = DataFile.open("sampledata.txt"); + * } catch (IOException e) { + * // stop test and ignore if data can't be opened + * assumeNoException(e); + * } + * // ... + * } + * </pre> + * @param t if non-null, the offending exception + */ + public static void assumeNoException(Throwable t) { + assumeThat(t, nullValue()); + } +} diff --git a/junit4/src/main/java/org/junit/Before.java b/junit4/src/main/java/org/junit/Before.java new file mode 100644 index 0000000..66b34ee --- /dev/null +++ b/junit4/src/main/java/org/junit/Before.java @@ -0,0 +1,39 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>When writing tests, it is common to find that several tests need similar + * objects created before they can run. Annotating a <code>public void</code> method + * with <code>@Before</code> causes that method to be run before the {@link org.junit.Test} method. + * The <code>@Before</code> methods of superclasses will be run before those of the current class. + * No other ordering is defined. + * </p> + * + * Here is a simple example: + * <pre> + * public class Example { + * List empty; + * @Before public void initialize() { + * empty= new ArrayList(); + * } + * @Test public void size() { + * ... + * } + * @Test public void remove() { + * ... + * } + * } + * </pre> + * + * @see org.junit.BeforeClass + * @see org.junit.After + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Before { +} + diff --git a/junit4/src/main/java/org/junit/BeforeClass.java b/junit4/src/main/java/org/junit/BeforeClass.java new file mode 100644 index 0000000..35b7854 --- /dev/null +++ b/junit4/src/main/java/org/junit/BeforeClass.java @@ -0,0 +1,35 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>Sometimes several tests need to share computationally expensive setup + * (like logging into a database). While this can compromise the independence of + * tests, sometimes it is a necessary optimization. Annotating a <code>public static void</code> no-arg method + * with <code>@BeforeClass</code> causes it to be run once before any of + * the test methods in the class. The <code>@BeforeClass</code> methods of superclasses + * will be run before those the current class.</p> + * + * For example: + * <pre> + * public class Example { + * @BeforeClass public static void onlyOnce() { + * ... + * } + * @Test public void one() { + * ... + * } + * @Test public void two() { + * ... + * } + * } + * </pre> + * @see org.junit.AfterClass + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface BeforeClass { +} diff --git a/junit4/src/main/java/org/junit/ClassRule.java b/junit4/src/main/java/org/junit/ClassRule.java new file mode 100644 index 0000000..97a111f --- /dev/null +++ b/junit4/src/main/java/org/junit/ClassRule.java @@ -0,0 +1,60 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates static fields that contain rules. Such a field must be public, + * static, and a subtype of {@link org.junit.rules.TestRule}. + * The {@link org.junit.runners.model.Statement} passed + * to the {@link org.junit.rules.TestRule} will run any {@link BeforeClass} methods, + * then the entire body of the test class (all contained methods, if it is + * a standard JUnit test class, or all contained classes, if it is a + * {@link org.junit.runners.Suite}), and finally any {@link AfterClass} methods. + * + * The statement passed to the {@link org.junit.rules.TestRule} will never throw an exception, + * and throwing an exception from the {@link org.junit.rules.TestRule} will result in undefined + * behavior. This means that some {@link org.junit.rules.TestRule}s, such as + * {@link org.junit.rules.ErrorCollector}, + * {@link org.junit.rules.ExpectedException}, + * and {@link org.junit.rules.Timeout}, + * have undefined behavior when used as {@link ClassRule}s. + * + * If there are multiple + * annotated {@link ClassRule}s on a class, they will be applied in an order + * that depends on your JVM's implementation of the reflection API, which is + * undefined, in general. + * + * For example, here is a test suite that connects to a server once before + * all the test classes run, and disconnects after they are finished: + * + * <pre> + * + * @RunWith(Suite.class) + * @SuiteClasses({A.class, B.class, C.class}) + * public class UsesExternalResource { + * public static Server myServer= new Server(); + * + * @ClassRule + * public static ExternalResource resource= new ExternalResource() { + * @Override + * protected void before() throws Throwable { + * myServer.connect(); + * }; + * + * @Override + * protected void after() { + * myServer.disconnect(); + * }; + * }; + * } + * </pre> + * + * For more information and more examples, see {@link org.junit.rules.TestRule}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface ClassRule { +} diff --git a/junit4/src/main/java/org/junit/ComparisonFailure.java b/junit4/src/main/java/org/junit/ComparisonFailure.java new file mode 100644 index 0000000..d37db4f --- /dev/null +++ b/junit4/src/main/java/org/junit/ComparisonFailure.java @@ -0,0 +1,138 @@ +package org.junit; + +/** + * Thrown when an {@link org.junit.Assert#assertEquals(Object, Object) assertEquals(String, String)} fails. Create and throw + * a <code>ComparisonFailure</code> manually if you want to show users the difference between two complex + * strings. + * + * Inspired by a patch from Alex Chaffee (alex@purpletech.com) + */ +public class ComparisonFailure extends AssertionError { + /** + * The maximum length for fExpected and fActual. If it is exceeded, the strings should be shortened. + * @see ComparisonCompactor + */ + private static final int MAX_CONTEXT_LENGTH= 20; + private static final long serialVersionUID= 1L; + + private String fExpected; + private String fActual; + + /** + * Constructs a comparison failure. + * @param message the identifying message or null + * @param expected the expected string value + * @param actual the actual string value + */ + public ComparisonFailure (String message, String expected, String actual) { + super (message); + fExpected= expected; + fActual= actual; + } + + /** + * Returns "..." in place of common prefix and "..." in + * place of common suffix between expected and actual. + * + * @see Throwable#getMessage() + */ + @Override + public String getMessage() { + return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage()); + } + + /** + * Returns the actual string value + * @return the actual string value + */ + public String getActual() { + return fActual; + } + /** + * Returns the expected string value + * @return the expected string value + */ + public String getExpected() { + return fExpected; + } + + private static class ComparisonCompactor { + private static final String ELLIPSIS= "..."; + private static final String DELTA_END= "]"; + private static final String DELTA_START= "["; + + /** + * The maximum length for <code>expected</code> and <code>actual</code>. When <code>contextLength</code> + * is exceeded, the Strings are shortened + */ + private int fContextLength; + private String fExpected; + private String fActual; + private int fPrefix; + private int fSuffix; + + /** + * @param contextLength the maximum length for <code>expected</code> and <code>actual</code>. When contextLength + * is exceeded, the Strings are shortened + * @param expected the expected string value + * @param actual the actual string value + */ + public ComparisonCompactor(int contextLength, String expected, String actual) { + fContextLength= contextLength; + fExpected= expected; + fActual= actual; + } + + private String compact(String message) { + if (fExpected == null || fActual == null || areStringsEqual()) + return Assert.format(message, fExpected, fActual); + + findCommonPrefix(); + findCommonSuffix(); + String expected= compactString(fExpected); + String actual= compactString(fActual); + return Assert.format(message, expected, actual); + } + + private String compactString(String source) { + String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END; + if (fPrefix > 0) + result= computeCommonPrefix() + result; + if (fSuffix > 0) + result= result + computeCommonSuffix(); + return result; + } + + private void findCommonPrefix() { + fPrefix= 0; + int end= Math.min(fExpected.length(), fActual.length()); + for (; fPrefix < end; fPrefix++) { + if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix)) + break; + } + } + + private void findCommonSuffix() { + int expectedSuffix= fExpected.length() - 1; + int actualSuffix= fActual.length() - 1; + for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) { + if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix)) + break; + } + fSuffix= fExpected.length() - expectedSuffix; + } + + private String computeCommonPrefix() { + return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix); + } + + private String computeCommonSuffix() { + int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length()); + return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : ""); + } + + private boolean areStringsEqual() { + return fExpected.equals(fActual); + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/Ignore.java b/junit4/src/main/java/org/junit/Ignore.java new file mode 100644 index 0000000..de530a9 --- /dev/null +++ b/junit4/src/main/java/org/junit/Ignore.java @@ -0,0 +1,39 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>Sometimes you want to temporarily disable a test or a group of tests. Methods annotated with + * {@link org.junit.Test} that are also annotated with <code>@Ignore</code> will not be executed as tests. + * Also, you can annotate a class containing test methods with <code>@Ignore</code> and none of the containing + * tests will be executed. Native JUnit 4 test runners should report the number of ignored tests along with the + * number of tests that ran and the number of tests that failed.</p> + * + * For example: + * <pre> + * @Ignore @Test public void something() { ... + * </pre> + * @Ignore takes an optional default parameter if you want to record why a test is being ignored:<br/> + * <pre> + * @Ignore("not ready yet") @Test public void something() { ... + * </pre> + * @Ignore can also be applied to the test class:<br/> + * <pre> + * @Ignore public class IgnoreMe { + * @Test public void test1() { ... } + * @Test public void test2() { ... } + * } + * </pre> + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface Ignore { + /** + * The optional reason why the test is ignored. + */ + String value() default ""; +} diff --git a/junit4/src/main/java/org/junit/Rule.java b/junit4/src/main/java/org/junit/Rule.java new file mode 100644 index 0000000..9e67c07 --- /dev/null +++ b/junit4/src/main/java/org/junit/Rule.java @@ -0,0 +1,47 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates fields that contain rules. Such a field must be public, not + * static, and a subtype of {@link org.junit.rules.TestRule}. + * The {@link org.junit.runners.model.Statement} passed + * to the {@link org.junit.rules.TestRule} will run any {@link Before} methods, + * then the {@link Test} method, and finally any {@link After} methods, + * throwing an exception if any of these fail. If there are multiple + * annotated {@link Rule}s on a class, they will be applied in an order + * that depends on your JVM's implementation of the reflection API, which is + * undefined, in general. + * + * For example, here is a test class that creates a temporary folder before + * each test method, and deletes it after each: + * + * <pre> + * public static class HasTempFolder { + * @Rule + * public TemporaryFolder folder= new TemporaryFolder(); + * + * @Test + * public void testUsingTempFolder() throws IOException { + * File createdFile= folder.newFile("myfile.txt"); + * File createdFolder= folder.newFolder("subfolder"); + * // ... + * } + * } + * </pre> + * + * For more information and more examples, see + * {@link org.junit.rules.TestRule}. + * + * Note: for backwards compatibility, this annotation may also mark + * fields of type {@link org.junit.rules.MethodRule}, which will be honored. However, + * this is a deprecated interface and feature. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface Rule { + +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/Test.java b/junit4/src/main/java/org/junit/Test.java new file mode 100644 index 0000000..23dc78a --- /dev/null +++ b/junit4/src/main/java/org/junit/Test.java @@ -0,0 +1,68 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>The <code>Test</code> annotation tells JUnit that the <code>public void</code> method + * to which it is attached can be run as a test case. To run the method, + * JUnit first constructs a fresh instance of the class then invokes the + * annotated method. Any exceptions thrown by the test will be reported + * by JUnit as a failure. If no exceptions are thrown, the test is assumed + * to have succeeded.</p> + * + * <p>A simple test looks like this: + * <pre> + * public class Example { + * <b>@Test</b> + * public void method() { + * org.junit.Assert.assertTrue( new ArrayList().isEmpty() ); + * } + * } + * </pre> + * </p> + * + * <p>The <code>Test</code> annotation supports two optional parameters. + * The first, <code>expected</code>, declares that a test method should throw + * an exception. If it doesn't throw an exception or if it throws a different exception + * than the one declared, the test fails. For example, the following test succeeds: + * <pre> + * @Test(<b>expected=IndexOutOfBoundsException.class</b>) public void outOfBounds() { + * new ArrayList<Object>().get(1); + * } + * </pre></p> + * + * <p>The second optional parameter, <code>timeout</code>, causes a test to fail if it takes + * longer than a specified amount of clock time (measured in milliseconds). The following test fails: + * <pre> + * @Test(<b>timeout=100</b>) public void infinity() { + * while(true); + * } + * </pre></p> + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface Test { + + /** + * Default empty exception + */ + static class None extends Throwable { + private static final long serialVersionUID= 1L; + private None() { + } + } + + /** + * Optionally specify <code>expected</code>, a Throwable, to cause a test method to succeed iff + * an exception of the specified class is thrown by the method. + */ + Class<? extends Throwable> expected() default None.class; + + /** + * Optionally specify <code>timeout</code> in milliseconds to cause a test method to fail if it + * takes longer than that number of milliseconds.*/ + long timeout() default 0L; +} diff --git a/junit4/src/main/java/org/junit/experimental/ParallelComputer.java b/junit4/src/main/java/org/junit/experimental/ParallelComputer.java new file mode 100644 index 0000000..fccb97c --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/ParallelComputer.java @@ -0,0 +1,78 @@ +package org.junit.experimental; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.junit.runner.Computer; +import org.junit.runner.Runner; +import org.junit.runners.ParentRunner; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; +import org.junit.runners.model.RunnerScheduler; + +public class ParallelComputer extends Computer { + private final boolean fClasses; + + private final boolean fMethods; + + public ParallelComputer(boolean classes, boolean methods) { + fClasses= classes; + fMethods= methods; + } + + public static Computer classes() { + return new ParallelComputer(true, false); + } + + public static Computer methods() { + return new ParallelComputer(false, true); + } + + private static <T> Runner parallelize(Runner runner) { + if (runner instanceof ParentRunner<?>) { + ((ParentRunner<?>) runner).setScheduler(new RunnerScheduler() { + private final List<Future<Object>> fResults= new ArrayList<Future<Object>>(); + + private final ExecutorService fService= Executors + .newCachedThreadPool(); + + public void schedule(final Runnable childStatement) { + fResults.add(fService.submit(new Callable<Object>() { + public Object call() throws Exception { + childStatement.run(); + return null; + } + })); + } + + public void finished() { + for (Future<Object> each : fResults) + try { + each.get(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + return runner; + } + + @Override + public Runner getSuite(RunnerBuilder builder, java.lang.Class<?>[] classes) + throws InitializationError { + Runner suite= super.getSuite(builder, classes); + return fClasses ? parallelize(suite) : suite; + } + + @Override + protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) + throws Throwable { + Runner runner= super.getRunner(builder, testClass); + return fMethods ? parallelize(runner) : runner; + } +} diff --git a/junit4/src/main/java/org/junit/experimental/categories/Categories.java b/junit4/src/main/java/org/junit/experimental/categories/Categories.java new file mode 100644 index 0000000..d57b4d3 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/categories/Categories.java @@ -0,0 +1,192 @@ +/** + * + */ +package org.junit.experimental.categories; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.runner.Description; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runners.Suite; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +/** + * From a given set of test classes, runs only the classes and methods that are + * annotated with either the category given with the @IncludeCategory + * annotation, or a subtype of that category. + * + * Note that, for now, annotating suites with {@code @Category} has no effect. + * Categories must be annotated on the direct method or class. + * + * Example: + * + * <pre> + * public interface FastTests { + * } + * + * public interface SlowTests { + * } + * + * public static class A { + * @Test + * public void a() { + * fail(); + * } + * + * @Category(SlowTests.class) + * @Test + * public void b() { + * } + * } + * + * @Category( { SlowTests.class, FastTests.class }) + * public static class B { + * @Test + * public void c() { + * + * } + * } + * + * @RunWith(Categories.class) + * @IncludeCategory(SlowTests.class) + * @SuiteClasses( { A.class, B.class }) + * // Note that Categories is a kind of Suite + * public static class SlowTestSuite { + * } + * </pre> + */ +public class Categories extends Suite { + // the way filters are implemented makes this unnecessarily complicated, + // buggy, and difficult to specify. A new way of handling filters could + // someday enable a better new implementation. + // https://github.com/KentBeck/junit/issues/issue/172 + + @Retention(RetentionPolicy.RUNTIME) + public @interface IncludeCategory { + public Class<?> value(); + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface ExcludeCategory { + public Class<?> value(); + } + + public static class CategoryFilter extends Filter { + public static CategoryFilter include(Class<?> categoryType) { + return new CategoryFilter(categoryType, null); + } + + private final Class<?> fIncluded; + + private final Class<?> fExcluded; + + public CategoryFilter(Class<?> includedCategory, + Class<?> excludedCategory) { + fIncluded= includedCategory; + fExcluded= excludedCategory; + } + + @Override + public String describe() { + return "category " + fIncluded; + } + + @Override + public boolean shouldRun(Description description) { + if (hasCorrectCategoryAnnotation(description)) + return true; + for (Description each : description.getChildren()) + if (shouldRun(each)) + return true; + return false; + } + + private boolean hasCorrectCategoryAnnotation(Description description) { + List<Class<?>> categories= categories(description); + if (categories.isEmpty()) + return fIncluded == null; + for (Class<?> each : categories) + if (fExcluded != null && fExcluded.isAssignableFrom(each)) + return false; + for (Class<?> each : categories) + if (fIncluded == null || fIncluded.isAssignableFrom(each)) + return true; + return false; + } + + private List<Class<?>> categories(Description description) { + ArrayList<Class<?>> categories= new ArrayList<Class<?>>(); + categories.addAll(Arrays.asList(directCategories(description))); + categories.addAll(Arrays.asList(directCategories(parentDescription(description)))); + return categories; + } + + private Description parentDescription(Description description) { + Class<?> testClass= description.getTestClass(); + if (testClass == null) + return null; + return Description.createSuiteDescription(testClass); + } + + private Class<?>[] directCategories(Description description) { + if (description == null) + return new Class<?>[0]; + Category annotation= description.getAnnotation(Category.class); + if (annotation == null) + return new Class<?>[0]; + return annotation.value(); + } + } + + public Categories(Class<?> klass, RunnerBuilder builder) + throws InitializationError { + super(klass, builder); + try { + filter(new CategoryFilter(getIncludedCategory(klass), + getExcludedCategory(klass))); + } catch (NoTestsRemainException e) { + throw new InitializationError(e); + } + assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription()); + } + + private Class<?> getIncludedCategory(Class<?> klass) { + IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); + return annotation == null ? null : annotation.value(); + } + + private Class<?> getExcludedCategory(Class<?> klass) { + ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); + return annotation == null ? null : annotation.value(); + } + + private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { + if (!canHaveCategorizedChildren(description)) + assertNoDescendantsHaveCategoryAnnotations(description); + for (Description each : description.getChildren()) + assertNoCategorizedDescendentsOfUncategorizeableParents(each); + } + + private void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError { + for (Description each : description.getChildren()) { + if (each.getAnnotation(Category.class) != null) + throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods."); + assertNoDescendantsHaveCategoryAnnotations(each); + } + } + + // If children have names like [0], our current magical category code can't determine their + // parentage. + private static boolean canHaveCategorizedChildren(Description description) { + for (Description each : description.getChildren()) + if (each.getTestClass() == null) + return false; + return true; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/categories/Category.java b/junit4/src/main/java/org/junit/experimental/categories/Category.java new file mode 100644 index 0000000..3a4c0b9 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/categories/Category.java @@ -0,0 +1,43 @@ +package org.junit.experimental.categories; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Marks a test class or test method as belonging to one or more categories of tests. + * The value is an array of arbitrary classes. + * + * This annotation is only interpreted by the Categories runner (at present). + * + * For example: +<pre> + public interface FastTests {} + public interface SlowTests {} + + public static class A { + @Test + public void a() { + fail(); + } + + @Category(SlowTests.class) + @Test + public void b() { + } + } + + @Category({SlowTests.class, FastTests.class}) + public static class B { + @Test + public void c() { + + } + } +</pre> + * + * For more usage, see code example on {@link Categories}. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface Category { + Class<?>[] value(); +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/max/CouldNotReadCoreException.java b/junit4/src/main/java/org/junit/experimental/max/CouldNotReadCoreException.java new file mode 100644 index 0000000..03c3c8c --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/max/CouldNotReadCoreException.java @@ -0,0 +1,15 @@ +package org.junit.experimental.max; + +/** + * Thrown when Max cannot read the MaxCore serialization + */ +public class CouldNotReadCoreException extends Exception { + private static final long serialVersionUID= 1L; + + /** + * Constructs + */ + public CouldNotReadCoreException(Throwable e) { + super(e); + } +} diff --git a/junit4/src/main/java/org/junit/experimental/max/MaxCore.java b/junit4/src/main/java/org/junit/experimental/max/MaxCore.java new file mode 100644 index 0000000..a2a34a9 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/max/MaxCore.java @@ -0,0 +1,170 @@ +package org.junit.experimental.max; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestSuite; + +import org.junit.internal.requests.SortingRequest; +import org.junit.internal.runners.ErrorReportingRunner; +import org.junit.internal.runners.JUnit38ClassRunner; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.Runner; +import org.junit.runners.Suite; +import org.junit.runners.model.InitializationError; + +/** + * A replacement for JUnitCore, which keeps track of runtime and failure history, and reorders tests + * to maximize the chances that a failing test occurs early in the test run. + * + * The rules for sorting are: + * <ol> + * <li> Never-run tests first, in arbitrary order + * <li> Group remaining tests by the date at which they most recently failed. + * <li> Sort groups such that the most recent failure date is first, and never-failing tests are at the end. + * <li> Within a group, run the fastest tests first. + * </ol> + */ +public class MaxCore { + private static final String MALFORMED_JUNIT_3_TEST_CLASS_PREFIX= "malformed JUnit 3 test class: "; + + /** + * Create a new MaxCore from a serialized file stored at storedResults + * @deprecated use storedLocally() + */ + @Deprecated + public static MaxCore forFolder(String folderName) { + return storedLocally(new File(folderName)); + } + + /** + * Create a new MaxCore from a serialized file stored at storedResults + */ + public static MaxCore storedLocally(File storedResults) { + return new MaxCore(storedResults); + } + + private final MaxHistory fHistory; + + private MaxCore(File storedResults) { + fHistory = MaxHistory.forFolder(storedResults); + } + + /** + * Run all the tests in <code>class</code>. + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public Result run(Class<?> testClass) { + return run(Request.aClass(testClass)); + } + + /** + * Run all the tests contained in <code>request</code>. + * @param request the request describing tests + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public Result run(Request request) { + return run(request, new JUnitCore()); + } + + /** + * Run all the tests contained in <code>request</code>. + * + * This variant should be used if {@code core} has attached listeners that this + * run should notify. + * + * @param request the request describing tests + * @param core a JUnitCore to delegate to. + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public Result run(Request request, JUnitCore core) { + core.addListener(fHistory.listener()); + return core.run(sortRequest(request).getRunner()); + } + + /** + * @param request + * @return a new Request, which contains all of the same tests, but in a new order. + */ + public Request sortRequest(Request request) { + if (request instanceof SortingRequest) // We'll pay big karma points for this + return request; + List<Description> leaves= findLeaves(request); + Collections.sort(leaves, fHistory.testComparator()); + return constructLeafRequest(leaves); + } + + private Request constructLeafRequest(List<Description> leaves) { + final List<Runner> runners = new ArrayList<Runner>(); + for (Description each : leaves) + runners.add(buildRunner(each)); + return new Request() { + @Override + public Runner getRunner() { + try { + return new Suite((Class<?>)null, runners) {}; + } catch (InitializationError e) { + return new ErrorReportingRunner(null, e); + } + } + }; + } + + private Runner buildRunner(Description each) { + if (each.toString().equals("TestSuite with 0 tests")) + return Suite.emptySuite(); + if (each.toString().startsWith(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX)) + // This is cheating, because it runs the whole class + // to get the warning for this method, but we can't do better, + // because JUnit 3.8's + // thrown away which method the warning is for. + return new JUnit38ClassRunner(new TestSuite(getMalformedTestClass(each))); + Class<?> type= each.getTestClass(); + if (type == null) + throw new RuntimeException("Can't build a runner from description [" + each + "]"); + String methodName= each.getMethodName(); + if (methodName == null) + return Request.aClass(type).getRunner(); + return Request.method(type, methodName).getRunner(); + } + + private Class<?> getMalformedTestClass(Description each) { + try { + return Class.forName(each.toString().replace(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX, "")); + } catch (ClassNotFoundException e) { + return null; + } + } + + /** + * @param request a request to run + * @return a list of method-level tests to run, sorted in the order + * specified in the class comment. + */ + public List<Description> sortedLeavesForTest(Request request) { + return findLeaves(sortRequest(request)); + } + + private List<Description> findLeaves(Request request) { + List<Description> results= new ArrayList<Description>(); + findLeaves(null, request.getRunner().getDescription(), results); + return results; + } + + private void findLeaves(Description parent, Description description, List<Description> results) { + if (description.getChildren().isEmpty()) + if (description.toString().equals("warning(junit.framework.TestSuite$1)")) + results.add(Description.createSuiteDescription(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX + parent)); + else + results.add(description); + else + for (Description each : description.getChildren()) + findLeaves(description, each, results); + } +} + diff --git a/junit4/src/main/java/org/junit/experimental/max/MaxHistory.java b/junit4/src/main/java/org/junit/experimental/max/MaxHistory.java new file mode 100644 index 0000000..e091793 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/max/MaxHistory.java @@ -0,0 +1,166 @@ +package org.junit.experimental.max; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +import org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +/** + * Stores a subset of the history of each test: + * <ul> + * <li>Last failure timestamp + * <li>Duration of last execution + * </ul> + */ +public class MaxHistory implements Serializable { + private static final long serialVersionUID= 1L; + + /** + * Loads a {@link MaxHistory} from {@code file}, or generates a new one that + * will be saved to {@code file}. + */ + public static MaxHistory forFolder(File file) { + if (file.exists()) + try { + return readHistory(file); + } catch (CouldNotReadCoreException e) { + e.printStackTrace(); + file.delete(); + } + return new MaxHistory(file); + } + + private static MaxHistory readHistory(File storedResults) + throws CouldNotReadCoreException { + try { + FileInputStream file= new FileInputStream(storedResults); + try { + ObjectInputStream stream= new ObjectInputStream(file); + try { + return (MaxHistory) stream.readObject(); + } finally { + stream.close(); + } + } finally { + file.close(); + } + } catch (Exception e) { + throw new CouldNotReadCoreException(e); + } + } + + private final Map<String, Long> fDurations= new HashMap<String, Long>(); + + private final Map<String, Long> fFailureTimestamps= new HashMap<String, Long>(); + + private final File fHistoryStore; + + private MaxHistory(File storedResults) { + fHistoryStore= storedResults; + } + + private void save() throws IOException { + ObjectOutputStream stream= new ObjectOutputStream(new FileOutputStream( + fHistoryStore)); + stream.writeObject(this); + stream.close(); + } + + Long getFailureTimestamp(Description key) { + return fFailureTimestamps.get(key.toString()); + } + + void putTestFailureTimestamp(Description key, long end) { + fFailureTimestamps.put(key.toString(), end); + } + + boolean isNewTest(Description key) { + return !fDurations.containsKey(key.toString()); + } + + Long getTestDuration(Description key) { + return fDurations.get(key.toString()); + } + + void putTestDuration(Description description, long duration) { + fDurations.put(description.toString(), duration); + } + + private final class RememberingListener extends RunListener { + private long overallStart= System.currentTimeMillis(); + + private Map<Description, Long> starts= new HashMap<Description, Long>(); + + @Override + public void testStarted(Description description) throws Exception { + starts.put(description, System.nanoTime()); // Get most accurate + // possible time + } + + @Override + public void testFinished(Description description) throws Exception { + long end= System.nanoTime(); + long start= starts.get(description); + putTestDuration(description, end - start); + } + + @Override + public void testFailure(Failure failure) throws Exception { + putTestFailureTimestamp(failure.getDescription(), overallStart); + } + + @Override + public void testRunFinished(Result result) throws Exception { + save(); + } + } + + private class TestComparator implements Comparator<Description> { + public int compare(Description o1, Description o2) { + // Always prefer new tests + if (isNewTest(o1)) + return -1; + if (isNewTest(o2)) + return 1; + // Then most recently failed first + int result= getFailure(o2).compareTo(getFailure(o1)); + return result != 0 ? result + // Then shorter tests first + : getTestDuration(o1).compareTo(getTestDuration(o2)); + } + + private Long getFailure(Description key) { + Long result= getFailureTimestamp(key); + if (result == null) + return 0L; // 0 = "never failed (that I know about)" + return result; + } + } + + /** + * @return a listener that will update this history based on the test + * results reported. + */ + public RunListener listener() { + return new RememberingListener(); + } + + /** + * @return a comparator that ranks tests based on the JUnit Max sorting + * rules, as described in the {@link MaxCore} class comment. + */ + public Comparator<Description> testComparator() { + return new TestComparator(); + } +} diff --git a/junit4/src/main/java/org/junit/experimental/results/FailureList.java b/junit4/src/main/java/org/junit/experimental/results/FailureList.java new file mode 100644 index 0000000..f4bc9b7 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/results/FailureList.java @@ -0,0 +1,31 @@ +/** + * + */ +package org.junit.experimental.results; + +import java.util.List; + +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +class FailureList { + private final List<Failure> failures; + + public FailureList(List<Failure> failures) { + this.failures= failures; + } + + public Result result() { + Result result= new Result(); + RunListener listener= result.createListener(); + for (Failure failure : failures) { + try { + listener.testFailure(failure); + } catch (Exception e) { + throw new RuntimeException("I can't believe this happened"); + } + } + return result; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/results/PrintableResult.java b/junit4/src/main/java/org/junit/experimental/results/PrintableResult.java new file mode 100644 index 0000000..8bc6f54 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/results/PrintableResult.java @@ -0,0 +1,63 @@ +package org.junit.experimental.results; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.List; + +import org.junit.internal.TextListener; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; + +/** + * A test result that prints nicely in error messages. + * This is only intended to be used in JUnit self-tests. + * For example: + * + * <pre> + * assertThat(testResult(HasExpectedException.class), isSuccessful()); + * </pre> + */ +public class PrintableResult { + /** + * The result of running JUnit on {@code type} + */ + public static PrintableResult testResult(Class<?> type) { + return testResult(Request.aClass(type)); + } + + /** + * The result of running JUnit on Request {@code request} + */ + public static PrintableResult testResult(Request request) { + return new PrintableResult(new JUnitCore().run(request)); + } + + private Result result; + + /** + * A result that includes the given {@code failures} + */ + public PrintableResult(List<Failure> failures) { + this(new FailureList(failures).result()); + } + + private PrintableResult(Result result) { + this.result = result; + } + + @Override + public String toString() { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + new TextListener(new PrintStream(stream)).testRunFinished(result); + return stream.toString(); + } + + /** + * Returns the number of failures in this result. + */ + public int failureCount() { + return result.getFailures().size(); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/results/ResultMatchers.java b/junit4/src/main/java/org/junit/experimental/results/ResultMatchers.java new file mode 100644 index 0000000..220d0dc --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/results/ResultMatchers.java @@ -0,0 +1,70 @@ +package org.junit.experimental.results; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.internal.matchers.TypeSafeMatcher; + +/** + * Matchers on a PrintableResult, to enable JUnit self-tests. + * For example: + * + * <pre> + * assertThat(testResult(HasExpectedException.class), isSuccessful()); + * </pre> + */ +public class ResultMatchers { + /** + * Matches if the tests are all successful + */ + public static Matcher<PrintableResult> isSuccessful() { + return failureCountIs(0); + } + + /** + * Matches if there are {@code count} failures + */ + public static Matcher<PrintableResult> failureCountIs(final int count) { + return new TypeSafeMatcher<PrintableResult>() { + public void describeTo(Description description) { + description.appendText("has " + count + " failures"); + } + + @Override + public boolean matchesSafely(PrintableResult item) { + return item.failureCount() == count; + } + }; + } + + /** + * Matches if the result has exactly one failure, and it contains {@code string} + */ + public static Matcher<Object> hasSingleFailureContaining(final String string) { + return new BaseMatcher<Object>() { + public boolean matches(Object item) { + return item.toString().contains(string) && failureCountIs(1).matches(item); + } + + public void describeTo(Description description) { + description.appendText("has single failure containing " + string); + } + }; + } + + /** + * Matches if the result has one or more failures, and at least one of them + * contains {@code string} + */ + public static Matcher<PrintableResult> hasFailureContaining(final String string) { + return new BaseMatcher<PrintableResult>() { + public boolean matches(Object item) { + return item.toString().contains(string); + } + + public void describeTo(Description description) { + description.appendText("has failure containing " + string); + } + }; + } +} diff --git a/junit4/src/main/java/org/junit/experimental/runners/Enclosed.java b/junit4/src/main/java/org/junit/experimental/runners/Enclosed.java new file mode 100644 index 0000000..b0560ed --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/runners/Enclosed.java @@ -0,0 +1,31 @@ +package org.junit.experimental.runners; + +import org.junit.runners.Suite; +import org.junit.runners.model.RunnerBuilder; + + +/** + * If you put tests in inner classes, Ant, for example, won't find them. By running the outer class + * with Enclosed, the tests in the inner classes will be run. You might put tests in inner classes + * to group them for convenience or to share constants. + * + * So, for example: + * <pre> + * \@RunWith(Enclosed.class) + * public class ListTests { + * ...useful shared stuff... + * public static class OneKindOfListTest {...} + * public static class AnotherKind {...} + * } + * </pre> + * + * For a real example, @see org.junit.tests.manipulation.SortableTest. + */ +public class Enclosed extends Suite { + /** + * Only called reflectively. Do not use programmatically. + */ + public Enclosed(Class<?> klass, RunnerBuilder builder) throws Throwable { + super(builder, klass, klass.getClasses()); + } +} diff --git a/junit4/src/main/java/org/junit/experimental/theories/DataPoint.java b/junit4/src/main/java/org/junit/experimental/theories/DataPoint.java new file mode 100644 index 0000000..2aaba6a --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/DataPoint.java @@ -0,0 +1,9 @@ +package org.junit.experimental.theories; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface DataPoint { + +} diff --git a/junit4/src/main/java/org/junit/experimental/theories/DataPoints.java b/junit4/src/main/java/org/junit/experimental/theories/DataPoints.java new file mode 100644 index 0000000..42145e3 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/DataPoints.java @@ -0,0 +1,9 @@ +package org.junit.experimental.theories; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface DataPoints { + +} diff --git a/junit4/src/main/java/org/junit/experimental/theories/ParameterSignature.java b/junit4/src/main/java/org/junit/experimental/theories/ParameterSignature.java new file mode 100644 index 0000000..e7150fc --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/ParameterSignature.java @@ -0,0 +1,90 @@ +/** + * + */ +package org.junit.experimental.theories; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParameterSignature { + public static ArrayList<ParameterSignature> signatures(Method method) { + return signatures(method.getParameterTypes(), method + .getParameterAnnotations()); + } + + public static List<ParameterSignature> signatures(Constructor<?> constructor) { + return signatures(constructor.getParameterTypes(), constructor + .getParameterAnnotations()); + } + + private static ArrayList<ParameterSignature> signatures( + Class<?>[] parameterTypes, Annotation[][] parameterAnnotations) { + ArrayList<ParameterSignature> sigs= new ArrayList<ParameterSignature>(); + for (int i= 0; i < parameterTypes.length; i++) { + sigs.add(new ParameterSignature(parameterTypes[i], + parameterAnnotations[i])); + } + return sigs; + } + + private final Class<?> type; + + private final Annotation[] annotations; + + private ParameterSignature(Class<?> type, Annotation[] annotations) { + this.type= type; + this.annotations= annotations; + } + + public boolean canAcceptType(Class<?> candidate) { + return type.isAssignableFrom(candidate); + } + + public Class<?> getType() { + return type; + } + + public List<Annotation> getAnnotations() { + return Arrays.asList(annotations); + } + + public boolean canAcceptArrayType(Class<?> type) { + return type.isArray() && canAcceptType(type.getComponentType()); + } + + public boolean hasAnnotation(Class<? extends Annotation> type) { + return getAnnotation(type) != null; + } + + public <T extends Annotation> T findDeepAnnotation(Class<T> annotationType) { + Annotation[] annotations2= annotations; + return findDeepAnnotation(annotations2, annotationType, 3); + } + + private <T extends Annotation> T findDeepAnnotation( + Annotation[] annotations, Class<T> annotationType, int depth) { + if (depth == 0) + return null; + for (Annotation each : annotations) { + if (annotationType.isInstance(each)) + return annotationType.cast(each); + Annotation candidate= findDeepAnnotation(each.annotationType() + .getAnnotations(), annotationType, depth - 1); + if (candidate != null) + return annotationType.cast(candidate); + } + + return null; + } + + public <T extends Annotation> T getAnnotation(Class<T> annotationType) { + for (Annotation each : getAnnotations()) + if (annotationType.isInstance(each)) + return annotationType.cast(each); + return null; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/theories/ParameterSupplier.java b/junit4/src/main/java/org/junit/experimental/theories/ParameterSupplier.java new file mode 100644 index 0000000..9016c91 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/ParameterSupplier.java @@ -0,0 +1,8 @@ +package org.junit.experimental.theories; + +import java.util.List; + + +public abstract class ParameterSupplier { + public abstract List<PotentialAssignment> getValueSources(ParameterSignature sig); +} diff --git a/junit4/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java b/junit4/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java new file mode 100644 index 0000000..8f090ef --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java @@ -0,0 +1,12 @@ +package org.junit.experimental.theories; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +@Retention(RetentionPolicy.RUNTIME) +public @interface ParametersSuppliedBy { + + Class<? extends ParameterSupplier> value(); + +} diff --git a/junit4/src/main/java/org/junit/experimental/theories/PotentialAssignment.java b/junit4/src/main/java/org/junit/experimental/theories/PotentialAssignment.java new file mode 100644 index 0000000..0c008d0 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/PotentialAssignment.java @@ -0,0 +1,31 @@ +package org.junit.experimental.theories; + +public abstract class PotentialAssignment { + public static class CouldNotGenerateValueException extends Exception { + private static final long serialVersionUID= 1L; + } + + public static PotentialAssignment forValue(final String name, final Object value) { + return new PotentialAssignment() { + @Override + public Object getValue() throws CouldNotGenerateValueException { + return value; + } + + @Override + public String toString() { + return String.format("[%s]", value); + } + + @Override + public String getDescription() + throws CouldNotGenerateValueException { + return name; + } + }; + } + + public abstract Object getValue() throws CouldNotGenerateValueException; + + public abstract String getDescription() throws CouldNotGenerateValueException; +} diff --git a/junit4/src/main/java/org/junit/experimental/theories/Theories.java b/junit4/src/main/java/org/junit/experimental/theories/Theories.java new file mode 100644 index 0000000..82ff98b --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/Theories.java @@ -0,0 +1,199 @@ +/** + * + */ +package org.junit.experimental.theories; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.experimental.theories.PotentialAssignment.CouldNotGenerateValueException; +import org.junit.experimental.theories.internal.Assignments; +import org.junit.experimental.theories.internal.ParameterizedAssertionError; +import org.junit.internal.AssumptionViolatedException; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +public class Theories extends BlockJUnit4ClassRunner { + public Theories(Class<?> klass) throws InitializationError { + super(klass); + } + + @Override + protected void collectInitializationErrors(List<Throwable> errors) { + super.collectInitializationErrors(errors); + validateDataPointFields(errors); + } + + private void validateDataPointFields(List<Throwable> errors) { + Field[] fields= getTestClass().getJavaClass().getDeclaredFields(); + + for (Field each : fields) + if (each.getAnnotation(DataPoint.class) != null && !Modifier.isStatic(each.getModifiers())) + errors.add(new Error("DataPoint field " + each.getName() + " must be static")); + } + + @Override + protected void validateConstructor(List<Throwable> errors) { + validateOnlyOneConstructor(errors); + } + + @Override + protected void validateTestMethods(List<Throwable> errors) { + for (FrameworkMethod each : computeTestMethods()) + if(each.getAnnotation(Theory.class) != null) + each.validatePublicVoid(false, errors); + else + each.validatePublicVoidNoArg(false, errors); + } + + @Override + protected List<FrameworkMethod> computeTestMethods() { + List<FrameworkMethod> testMethods= super.computeTestMethods(); + List<FrameworkMethod> theoryMethods= getTestClass().getAnnotatedMethods(Theory.class); + testMethods.removeAll(theoryMethods); + testMethods.addAll(theoryMethods); + return testMethods; + } + + @Override + public Statement methodBlock(final FrameworkMethod method) { + return new TheoryAnchor(method, getTestClass()); + } + + public static class TheoryAnchor extends Statement { + private int successes= 0; + + private FrameworkMethod fTestMethod; + private TestClass fTestClass; + + private List<AssumptionViolatedException> fInvalidParameters= new ArrayList<AssumptionViolatedException>(); + + public TheoryAnchor(FrameworkMethod method, TestClass testClass) { + fTestMethod= method; + fTestClass= testClass; + } + + private TestClass getTestClass() { + return fTestClass; + } + + @Override + public void evaluate() throws Throwable { + runWithAssignment(Assignments.allUnassigned( + fTestMethod.getMethod(), getTestClass())); + + if (successes == 0) + Assert + .fail("Never found parameters that satisfied method assumptions. Violated assumptions: " + + fInvalidParameters); + } + + protected void runWithAssignment(Assignments parameterAssignment) + throws Throwable { + if (!parameterAssignment.isComplete()) { + runWithIncompleteAssignment(parameterAssignment); + } else { + runWithCompleteAssignment(parameterAssignment); + } + } + + protected void runWithIncompleteAssignment(Assignments incomplete) + throws InstantiationException, IllegalAccessException, + Throwable { + for (PotentialAssignment source : incomplete + .potentialsForNextUnassigned()) { + runWithAssignment(incomplete.assignNext(source)); + } + } + + protected void runWithCompleteAssignment(final Assignments complete) + throws InstantiationException, IllegalAccessException, + InvocationTargetException, NoSuchMethodException, Throwable { + new BlockJUnit4ClassRunner(getTestClass().getJavaClass()) { + @Override + protected void collectInitializationErrors( + List<Throwable> errors) { + // do nothing + } + + @Override + public Statement methodBlock(FrameworkMethod method) { + final Statement statement= super.methodBlock(method); + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + statement.evaluate(); + handleDataPointSuccess(); + } catch (AssumptionViolatedException e) { + handleAssumptionViolation(e); + } catch (Throwable e) { + reportParameterizedError(e, complete + .getArgumentStrings(nullsOk())); + } + } + + }; + } + + @Override + protected Statement methodInvoker(FrameworkMethod method, Object test) { + return methodCompletesWithParameters(method, complete, test); + } + + @Override + public Object createTest() throws Exception { + return getTestClass().getOnlyConstructor().newInstance( + complete.getConstructorArguments(nullsOk())); + } + }.methodBlock(fTestMethod).evaluate(); + } + + private Statement methodCompletesWithParameters( + final FrameworkMethod method, final Assignments complete, final Object freshInstance) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + final Object[] values= complete.getMethodArguments( + nullsOk()); + method.invokeExplosively(freshInstance, values); + } catch (CouldNotGenerateValueException e) { + // ignore + } + } + }; + } + + protected void handleAssumptionViolation(AssumptionViolatedException e) { + fInvalidParameters.add(e); + } + + protected void reportParameterizedError(Throwable e, Object... params) + throws Throwable { + if (params.length == 0) + throw e; + throw new ParameterizedAssertionError(e, fTestMethod.getName(), + params); + } + + private boolean nullsOk() { + Theory annotation= fTestMethod.getMethod().getAnnotation( + Theory.class); + if (annotation == null) + return false; + return annotation.nullsAccepted(); + } + + protected void handleDataPointSuccess() { + successes++; + } + } +} diff --git a/junit4/src/main/java/org/junit/experimental/theories/Theory.java b/junit4/src/main/java/org/junit/experimental/theories/Theory.java new file mode 100644 index 0000000..134fe9d --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/Theory.java @@ -0,0 +1,12 @@ +/** + * + */ +package org.junit.experimental.theories; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Theory { + boolean nullsAccepted() default true; +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/theories/internal/AllMembersSupplier.java b/junit4/src/main/java/org/junit/experimental/theories/internal/AllMembersSupplier.java new file mode 100644 index 0000000..615cc3e --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/internal/AllMembersSupplier.java @@ -0,0 +1,127 @@ +/** + * + */ +package org.junit.experimental.theories.internal; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.ParameterSignature; +import org.junit.experimental.theories.ParameterSupplier; +import org.junit.experimental.theories.PotentialAssignment; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.TestClass; + +/** + * Supplies Theory parameters based on all public members of the target class. + */ +public class AllMembersSupplier extends ParameterSupplier { + static class MethodParameterValue extends PotentialAssignment { + private final FrameworkMethod fMethod; + + private MethodParameterValue(FrameworkMethod dataPointMethod) { + fMethod= dataPointMethod; + } + + @Override + public Object getValue() throws CouldNotGenerateValueException { + try { + return fMethod.invokeExplosively(null); + } catch (IllegalArgumentException e) { + throw new RuntimeException( + "unexpected: argument length is checked"); + } catch (IllegalAccessException e) { + throw new RuntimeException( + "unexpected: getMethods returned an inaccessible method"); + } catch (Throwable e) { + throw new CouldNotGenerateValueException(); + // do nothing, just look for more values + } + } + + @Override + public String getDescription() throws CouldNotGenerateValueException { + return fMethod.getName(); + } + } + + private final TestClass fClass; + + /** + * Constructs a new supplier for {@code type} + */ + public AllMembersSupplier(TestClass type) { + fClass= type; + } + + @Override + public List<PotentialAssignment> getValueSources(ParameterSignature sig) { + List<PotentialAssignment> list= new ArrayList<PotentialAssignment>(); + + addFields(sig, list); + addSinglePointMethods(sig, list); + addMultiPointMethods(list); + + return list; + } + + private void addMultiPointMethods(List<PotentialAssignment> list) { + for (FrameworkMethod dataPointsMethod : fClass + .getAnnotatedMethods(DataPoints.class)) + try { + addArrayValues(dataPointsMethod.getName(), list, dataPointsMethod.invokeExplosively(null)); + } catch (Throwable e) { + // ignore and move on + } + } + + @SuppressWarnings("deprecation") + private void addSinglePointMethods(ParameterSignature sig, + List<PotentialAssignment> list) { + for (FrameworkMethod dataPointMethod : fClass + .getAnnotatedMethods(DataPoint.class)) { + Class<?> type= sig.getType(); + if ((dataPointMethod.producesType(type))) + list.add(new MethodParameterValue(dataPointMethod)); + } + } + + private void addFields(ParameterSignature sig, + List<PotentialAssignment> list) { + for (final Field field : fClass.getJavaClass().getFields()) { + if (Modifier.isStatic(field.getModifiers())) { + Class<?> type= field.getType(); + if (sig.canAcceptArrayType(type) + && field.getAnnotation(DataPoints.class) != null) { + addArrayValues(field.getName(), list, getStaticFieldValue(field)); + } else if (sig.canAcceptType(type) + && field.getAnnotation(DataPoint.class) != null) { + list.add(PotentialAssignment + .forValue(field.getName(), getStaticFieldValue(field))); + } + } + } + } + + private void addArrayValues(String name, List<PotentialAssignment> list, Object array) { + for (int i= 0; i < Array.getLength(array); i++) + list.add(PotentialAssignment.forValue(name + "[" + i + "]", Array.get(array, i))); + } + + private Object getStaticFieldValue(final Field field) { + try { + return field.get(null); + } catch (IllegalArgumentException e) { + throw new RuntimeException( + "unexpected: field from getClass doesn't exist on object"); + } catch (IllegalAccessException e) { + throw new RuntimeException( + "unexpected: getFields returned an inaccessible field"); + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/theories/internal/Assignments.java b/junit4/src/main/java/org/junit/experimental/theories/internal/Assignments.java new file mode 100644 index 0000000..bd94f00 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/internal/Assignments.java @@ -0,0 +1,133 @@ +/** + * + */ +package org.junit.experimental.theories.internal; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.junit.experimental.theories.ParameterSignature; +import org.junit.experimental.theories.ParameterSupplier; +import org.junit.experimental.theories.ParametersSuppliedBy; +import org.junit.experimental.theories.PotentialAssignment; +import org.junit.experimental.theories.PotentialAssignment.CouldNotGenerateValueException; +import org.junit.runners.model.TestClass; + +/** + * A potentially incomplete list of value assignments for a method's formal + * parameters + */ +public class Assignments { + private List<PotentialAssignment> fAssigned; + + private final List<ParameterSignature> fUnassigned; + + private final TestClass fClass; + + private Assignments(List<PotentialAssignment> assigned, + List<ParameterSignature> unassigned, TestClass testClass) { + fUnassigned= unassigned; + fAssigned= assigned; + fClass= testClass; + } + + /** + * Returns a new assignment list for {@code testMethod}, with no params + * assigned. + */ + public static Assignments allUnassigned(Method testMethod, + TestClass testClass) throws Exception { + List<ParameterSignature> signatures; + signatures= ParameterSignature.signatures(testClass + .getOnlyConstructor()); + signatures.addAll(ParameterSignature.signatures(testMethod)); + return new Assignments(new ArrayList<PotentialAssignment>(), + signatures, testClass); + } + + public boolean isComplete() { + return fUnassigned.size() == 0; + } + + public ParameterSignature nextUnassigned() { + return fUnassigned.get(0); + } + + public Assignments assignNext(PotentialAssignment source) { + List<PotentialAssignment> assigned= new ArrayList<PotentialAssignment>( + fAssigned); + assigned.add(source); + + return new Assignments(assigned, fUnassigned.subList(1, fUnassigned + .size()), fClass); + } + + public Object[] getActualValues(int start, int stop, boolean nullsOk) + throws CouldNotGenerateValueException { + Object[] values= new Object[stop - start]; + for (int i= start; i < stop; i++) { + Object value= fAssigned.get(i).getValue(); + if (value == null && !nullsOk) + throw new CouldNotGenerateValueException(); + values[i - start]= value; + } + return values; + } + + public List<PotentialAssignment> potentialsForNextUnassigned() + throws InstantiationException, IllegalAccessException { + ParameterSignature unassigned= nextUnassigned(); + return getSupplier(unassigned).getValueSources(unassigned); + } + + public ParameterSupplier getSupplier(ParameterSignature unassigned) + throws InstantiationException, IllegalAccessException { + ParameterSupplier supplier= getAnnotatedSupplier(unassigned); + if (supplier != null) + return supplier; + + return new AllMembersSupplier(fClass); + } + + public ParameterSupplier getAnnotatedSupplier(ParameterSignature unassigned) + throws InstantiationException, IllegalAccessException { + ParametersSuppliedBy annotation= unassigned + .findDeepAnnotation(ParametersSuppliedBy.class); + if (annotation == null) + return null; + return annotation.value().newInstance(); + } + + public Object[] getConstructorArguments(boolean nullsOk) + throws CouldNotGenerateValueException { + return getActualValues(0, getConstructorParameterCount(), nullsOk); + } + + public Object[] getMethodArguments(boolean nullsOk) + throws CouldNotGenerateValueException { + return getActualValues(getConstructorParameterCount(), + fAssigned.size(), nullsOk); + } + + public Object[] getAllArguments(boolean nullsOk) + throws CouldNotGenerateValueException { + return getActualValues(0, fAssigned.size(), nullsOk); + } + + private int getConstructorParameterCount() { + List<ParameterSignature> signatures= ParameterSignature + .signatures(fClass.getOnlyConstructor()); + int constructorParameterCount= signatures.size(); + return constructorParameterCount; + } + + public Object[] getArgumentStrings(boolean nullsOk) + throws CouldNotGenerateValueException { + Object[] values= new Object[fAssigned.size()]; + for (int i= 0; i < values.length; i++) { + values[i]= fAssigned.get(i).getDescription(); + } + return values; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/theories/internal/ParameterizedAssertionError.java b/junit4/src/main/java/org/junit/experimental/theories/internal/ParameterizedAssertionError.java new file mode 100644 index 0000000..285bc3a --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/internal/ParameterizedAssertionError.java @@ -0,0 +1,49 @@ +/** + * + */ +package org.junit.experimental.theories.internal; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + + +public class ParameterizedAssertionError extends RuntimeException { + private static final long serialVersionUID = 1L; + + public ParameterizedAssertionError(Throwable targetException, + String methodName, Object... params) { + super(String.format("%s(%s)", methodName, join(", ", params)), + targetException); + } + + @Override public boolean equals(Object obj) { + return toString().equals(obj.toString()); + } + + public static String join(String delimiter, Object... params) { + return join(delimiter, Arrays.asList(params)); + } + + public static String join(String delimiter, + Collection<Object> values) { + StringBuffer buffer = new StringBuffer(); + Iterator<Object> iter = values.iterator(); + while (iter.hasNext()) { + Object next = iter.next(); + buffer.append(stringValueOf(next)); + if (iter.hasNext()) { + buffer.append(delimiter); + } + } + return buffer.toString(); + } + + private static String stringValueOf(Object next) { + try { + return String.valueOf(next); + } catch (Throwable e) { + return "[toString failed]"; + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/experimental/theories/suppliers/TestedOn.java b/junit4/src/main/java/org/junit/experimental/theories/suppliers/TestedOn.java new file mode 100644 index 0000000..d6ede64 --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/suppliers/TestedOn.java @@ -0,0 +1,13 @@ +package org.junit.experimental.theories.suppliers; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.experimental.theories.ParametersSuppliedBy; + + +@ParametersSuppliedBy(TestedOnSupplier.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestedOn { + int[] ints(); +} diff --git a/junit4/src/main/java/org/junit/experimental/theories/suppliers/TestedOnSupplier.java b/junit4/src/main/java/org/junit/experimental/theories/suppliers/TestedOnSupplier.java new file mode 100644 index 0000000..f80298f --- /dev/null +++ b/junit4/src/main/java/org/junit/experimental/theories/suppliers/TestedOnSupplier.java @@ -0,0 +1,23 @@ +package org.junit.experimental.theories.suppliers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.experimental.theories.ParameterSignature; +import org.junit.experimental.theories.ParameterSupplier; +import org.junit.experimental.theories.PotentialAssignment; + + + +public class TestedOnSupplier extends ParameterSupplier { + @Override public List<PotentialAssignment> getValueSources(ParameterSignature sig) { + List<PotentialAssignment> list = new ArrayList<PotentialAssignment>(); + TestedOn testedOn = sig.getAnnotation(TestedOn.class); + int[] ints = testedOn.ints(); + for (final int i : ints) { + list.add(PotentialAssignment.forValue(Arrays.asList(ints).toString(), i)); + } + return list; + } +} diff --git a/junit4/src/main/java/org/junit/internal/ArrayComparisonFailure.java b/junit4/src/main/java/org/junit/internal/ArrayComparisonFailure.java new file mode 100644 index 0000000..08851de --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/ArrayComparisonFailure.java @@ -0,0 +1,59 @@ +package org.junit.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; + +/** + * Thrown when two array elements differ + * @see Assert#assertArrayEquals(String, Object[], Object[]) + */ +public class ArrayComparisonFailure extends AssertionError { + + private static final long serialVersionUID= 1L; + + private List<Integer> fIndices= new ArrayList<Integer>(); + private final String fMessage; + private final AssertionError fCause; + + /** + * Construct a new <code>ArrayComparisonFailure</code> with an error text and the array's + * dimension that was not equal + * @param cause the exception that caused the array's content to fail the assertion test + * @param index the array position of the objects that are not equal. + * @see Assert#assertArrayEquals(String, Object[], Object[]) + */ + public ArrayComparisonFailure(String message, AssertionError cause, int index) { + fMessage= message; + fCause= cause; + addDimension(index); + } + + public void addDimension(int index) { + fIndices.add(0, index); + } + + @Override + public String getMessage() { + StringBuilder builder= new StringBuilder(); + if (fMessage != null) + builder.append(fMessage); + builder.append("arrays first differed at element "); + for (int each : fIndices) { + builder.append("["); + builder.append(each); + builder.append("]"); + } + builder.append("; "); + builder.append(fCause.getMessage()); + return builder.toString(); + } + + /** + * {@inheritDoc} + */ + @Override public String toString() { + return getMessage(); + } +} diff --git a/junit4/src/main/java/org/junit/internal/AssumptionViolatedException.java b/junit4/src/main/java/org/junit/internal/AssumptionViolatedException.java new file mode 100644 index 0000000..8e11268 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/AssumptionViolatedException.java @@ -0,0 +1,40 @@ +package org.junit.internal; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.SelfDescribing; +import org.hamcrest.StringDescription; + +public class AssumptionViolatedException extends RuntimeException implements SelfDescribing { + private static final long serialVersionUID= 1L; + + private final Object fValue; + + private final Matcher<?> fMatcher; + + public AssumptionViolatedException(Object value, Matcher<?> matcher) { + super(value instanceof Throwable ? (Throwable) value : null); + fValue= value; + fMatcher= matcher; + } + + public AssumptionViolatedException(String assumption) { + this(assumption, null); + } + + @Override + public String getMessage() { + return StringDescription.asString(this); + } + + public void describeTo(Description description) { + if (fMatcher != null) { + description.appendText("got: "); + description.appendValue(fValue); + description.appendText(", expected: "); + description.appendDescriptionOf(fMatcher); + } else { + description.appendText("failed assumption: " + fValue); + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/ComparisonCriteria.java b/junit4/src/main/java/org/junit/internal/ComparisonCriteria.java new file mode 100644 index 0000000..e97011d --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/ComparisonCriteria.java @@ -0,0 +1,76 @@ +package org.junit.internal; + +import java.lang.reflect.Array; + +import org.junit.Assert; + +/** + * Defines criteria for finding two items "equal enough". Concrete subclasses + * may demand exact equality, or, for example, equality within a given delta. + */ +public abstract class ComparisonCriteria { + /** + * Asserts that two arrays are equal, according to the criteria defined by + * the concrete subclass. If they are not, an {@link AssertionError} is + * thrown with the given message. If <code>expecteds</code> and + * <code>actuals</code> are <code>null</code>, they are considered equal. + * + * @param message + * the identifying message for the {@link AssertionError} ( + * <code>null</code> okay) + * @param expecteds + * Object array or array of arrays (multi-dimensional array) with + * expected values. + * @param actuals + * Object array or array of arrays (multi-dimensional array) with + * actual values + */ + public void arrayEquals(String message, Object expecteds, Object actuals) + throws ArrayComparisonFailure { + if (expecteds == actuals) + return; + String header= message == null ? "" : message + ": "; + + int expectedsLength= assertArraysAreSameLength(expecteds, + actuals, header); + + for (int i= 0; i < expectedsLength; i++) { + Object expected= Array.get(expecteds, i); + Object actual= Array.get(actuals, i); + + if (isArray(expected) && isArray(actual)) { + try { + arrayEquals(message, expected, actual); + } catch (ArrayComparisonFailure e) { + e.addDimension(i); + throw e; + } + } else + try { + assertElementsEqual(expected, actual); + } catch (AssertionError e) { + throw new ArrayComparisonFailure(header, e, i); + } + } + } + + private boolean isArray(Object expected) { + return expected != null && expected.getClass().isArray(); + } + + private int assertArraysAreSameLength(Object expecteds, + Object actuals, String header) { + if (expecteds == null) + Assert.fail(header + "expected array was null"); + if (actuals == null) + Assert.fail(header + "actual array was null"); + int actualsLength= Array.getLength(actuals); + int expectedsLength= Array.getLength(expecteds); + if (actualsLength != expectedsLength) + Assert.fail(header + "array lengths differed, expected.length=" + + expectedsLength + " actual.length=" + actualsLength); + return expectedsLength; + } + + protected abstract void assertElementsEqual(Object expected, Object actual); +} diff --git a/junit4/src/main/java/org/junit/internal/ExactComparisonCriteria.java b/junit4/src/main/java/org/junit/internal/ExactComparisonCriteria.java new file mode 100644 index 0000000..0a632ff --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/ExactComparisonCriteria.java @@ -0,0 +1,10 @@ +package org.junit.internal; + +import org.junit.Assert; + +public class ExactComparisonCriteria extends ComparisonCriteria { + @Override + protected void assertElementsEqual(Object expected, Object actual) { + Assert.assertEquals(expected, actual); + } +} diff --git a/junit4/src/main/java/org/junit/internal/InexactComparisonCriteria.java b/junit4/src/main/java/org/junit/internal/InexactComparisonCriteria.java new file mode 100644 index 0000000..ef3d7ff --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/InexactComparisonCriteria.java @@ -0,0 +1,19 @@ +package org.junit.internal; + +import org.junit.Assert; + +public class InexactComparisonCriteria extends ComparisonCriteria { + public double fDelta; + + public InexactComparisonCriteria(double delta) { + fDelta= delta; + } + + @Override + protected void assertElementsEqual(Object expected, Object actual) { + if (expected instanceof Double) + Assert.assertEquals((Double)expected, (Double)actual, fDelta); + else + Assert.assertEquals((Float)expected, (Float)actual, fDelta); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/JUnitSystem.java b/junit4/src/main/java/org/junit/internal/JUnitSystem.java new file mode 100644 index 0000000..6d9c242 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/JUnitSystem.java @@ -0,0 +1,8 @@ +package org.junit.internal; + +import java.io.PrintStream; + +public interface JUnitSystem { + void exit(int i); + PrintStream out(); +} diff --git a/junit4/src/main/java/org/junit/internal/RealSystem.java b/junit4/src/main/java/org/junit/internal/RealSystem.java new file mode 100644 index 0000000..1067c6d --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/RealSystem.java @@ -0,0 +1,15 @@ +package org.junit.internal; + +import java.io.PrintStream; + +public class RealSystem implements JUnitSystem { + + public void exit(int code) { + System.exit(code); + } + + public PrintStream out() { + return System.out; + } + +} diff --git a/junit4/src/main/java/org/junit/internal/TextListener.java b/junit4/src/main/java/org/junit/internal/TextListener.java new file mode 100644 index 0000000..2b1c679 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/TextListener.java @@ -0,0 +1,98 @@ +package org.junit.internal; + +import java.io.PrintStream; +import java.text.NumberFormat; +import java.util.List; + +import org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +public class TextListener extends RunListener { + + private final PrintStream fWriter; + + public TextListener(JUnitSystem system) { + this(system.out()); + } + + public TextListener(PrintStream writer) { + this.fWriter= writer; + } + + @Override + public void testRunFinished(Result result) { + printHeader(result.getRunTime()); + printFailures(result); + printFooter(result); + } + + @Override + public void testStarted(Description description) { + fWriter.append('.'); + } + + @Override + public void testFailure(Failure failure) { + fWriter.append('E'); + } + + @Override + public void testIgnored(Description description) { + fWriter.append('I'); + } + + /* + * Internal methods + */ + + private PrintStream getWriter() { + return fWriter; + } + + protected void printHeader(long runTime) { + getWriter().println(); + getWriter().println("Time: " + elapsedTimeAsString(runTime)); + } + + protected void printFailures(Result result) { + List<Failure> failures= result.getFailures(); + if (failures.size() == 0) + return; + if (failures.size() == 1) + getWriter().println("There was " + failures.size() + " failure:"); + else + getWriter().println("There were " + failures.size() + " failures:"); + int i= 1; + for (Failure each : failures) + printFailure(each, "" + i++); + } + + protected void printFailure(Failure each, String prefix) { + getWriter().println(prefix + ") " + each.getTestHeader()); + getWriter().print(each.getTrace()); + } + + protected void printFooter(Result result) { + if (result.wasSuccessful()) { + getWriter().println(); + getWriter().print("OK"); + getWriter().println(" (" + result.getRunCount() + " test" + (result.getRunCount() == 1 ? "" : "s") + ")"); + + } else { + getWriter().println(); + getWriter().println("FAILURES!!!"); + getWriter().println("Tests run: " + result.getRunCount() + ", Failures: " + result.getFailureCount()); + } + getWriter().println(); + } + + /** + * Returns the formatted string of the elapsed time. Duplicated from + * BaseTestRunner. Fix it. + */ + protected String elapsedTimeAsString(long runTime) { + return NumberFormat.getInstance().format((double) runTime / 1000); + } +} diff --git a/junit4/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java b/junit4/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java new file mode 100644 index 0000000..d3bd50a --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java @@ -0,0 +1,57 @@ +/** + * + */ +package org.junit.internal.builders; + +import java.util.Arrays; +import java.util.List; + +import org.junit.runner.Runner; +import org.junit.runners.model.RunnerBuilder; + +public class AllDefaultPossibilitiesBuilder extends RunnerBuilder { + private final boolean fCanUseSuiteMethod; + + public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) { + fCanUseSuiteMethod= canUseSuiteMethod; + } + + @Override + public Runner runnerForClass(Class<?> testClass) throws Throwable { + List<RunnerBuilder> builders= Arrays.asList( + ignoredBuilder(), + annotatedBuilder(), + suiteMethodBuilder(), + junit3Builder(), + junit4Builder()); + + for (RunnerBuilder each : builders) { + Runner runner= each.safeRunnerForClass(testClass); + if (runner != null) + return runner; + } + return null; + } + + protected JUnit4Builder junit4Builder() { + return new JUnit4Builder(); + } + + protected JUnit3Builder junit3Builder() { + return new JUnit3Builder(); + } + + protected AnnotatedBuilder annotatedBuilder() { + return new AnnotatedBuilder(this); + } + + protected IgnoredBuilder ignoredBuilder() { + return new IgnoredBuilder(); + } + + protected RunnerBuilder suiteMethodBuilder() { + if (fCanUseSuiteMethod) + return new SuiteMethodBuilder(); + return new NullBuilder(); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java b/junit4/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java new file mode 100644 index 0000000..8ed9ca7 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java @@ -0,0 +1,45 @@ +/** + * + */ +package org.junit.internal.builders; + +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +public class AnnotatedBuilder extends RunnerBuilder { + private static final String CONSTRUCTOR_ERROR_FORMAT= "Custom runner class %s should have a public constructor with signature %s(Class testClass)"; + + private RunnerBuilder fSuiteBuilder; + + public AnnotatedBuilder(RunnerBuilder suiteBuilder) { + fSuiteBuilder= suiteBuilder; + } + + @Override + public Runner runnerForClass(Class<?> testClass) throws Exception { + RunWith annotation= testClass.getAnnotation(RunWith.class); + if (annotation != null) + return buildRunner(annotation.value(), testClass); + return null; + } + + public Runner buildRunner(Class<? extends Runner> runnerClass, + Class<?> testClass) throws Exception { + try { + return runnerClass.getConstructor(Class.class).newInstance( + new Object[] { testClass }); + } catch (NoSuchMethodException e) { + try { + return runnerClass.getConstructor(Class.class, + RunnerBuilder.class).newInstance( + new Object[] { testClass, fSuiteBuilder }); + } catch (NoSuchMethodException e2) { + String simpleName= runnerClass.getSimpleName(); + throw new InitializationError(String.format( + CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName)); + } + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/builders/IgnoredBuilder.java b/junit4/src/main/java/org/junit/internal/builders/IgnoredBuilder.java new file mode 100644 index 0000000..6be342c --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/builders/IgnoredBuilder.java @@ -0,0 +1,17 @@ +/** + * + */ +package org.junit.internal.builders; + +import org.junit.Ignore; +import org.junit.runner.Runner; +import org.junit.runners.model.RunnerBuilder; + +public class IgnoredBuilder extends RunnerBuilder { + @Override + public Runner runnerForClass(Class<?> testClass) { + if (testClass.getAnnotation(Ignore.class) != null) + return new IgnoredClassRunner(testClass); + return null; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java b/junit4/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java new file mode 100644 index 0000000..b4200a8 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java @@ -0,0 +1,26 @@ +/** + * + */ +package org.junit.internal.builders; + +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; + +public class IgnoredClassRunner extends Runner { + private final Class<?> fTestClass; + + public IgnoredClassRunner(Class<?> testClass) { + fTestClass= testClass; + } + + @Override + public void run(RunNotifier notifier) { + notifier.fireTestIgnored(getDescription()); + } + + @Override + public Description getDescription() { + return Description.createSuiteDescription(fTestClass); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/builders/JUnit3Builder.java b/junit4/src/main/java/org/junit/internal/builders/JUnit3Builder.java new file mode 100644 index 0000000..ddb070b --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/builders/JUnit3Builder.java @@ -0,0 +1,21 @@ +/** + * + */ +package org.junit.internal.builders; + +import org.junit.internal.runners.JUnit38ClassRunner; +import org.junit.runner.Runner; +import org.junit.runners.model.RunnerBuilder; + +public class JUnit3Builder extends RunnerBuilder { + @Override + public Runner runnerForClass(Class<?> testClass) throws Throwable { + if (isPre4Test(testClass)) + return new JUnit38ClassRunner(testClass); + return null; + } + + boolean isPre4Test(Class<?> testClass) { + return junit.framework.TestCase.class.isAssignableFrom(testClass); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/builders/JUnit4Builder.java b/junit4/src/main/java/org/junit/internal/builders/JUnit4Builder.java new file mode 100644 index 0000000..4380db7 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/builders/JUnit4Builder.java @@ -0,0 +1,15 @@ +/** + * + */ +package org.junit.internal.builders; + +import org.junit.runner.Runner; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.RunnerBuilder; + +public class JUnit4Builder extends RunnerBuilder { + @Override + public Runner runnerForClass(Class<?> testClass) throws Throwable { + return new BlockJUnit4ClassRunner(testClass); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/builders/NullBuilder.java b/junit4/src/main/java/org/junit/internal/builders/NullBuilder.java new file mode 100644 index 0000000..9d43d69 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/builders/NullBuilder.java @@ -0,0 +1,14 @@ +/** + * + */ +package org.junit.internal.builders; + +import org.junit.runner.Runner; +import org.junit.runners.model.RunnerBuilder; + +public class NullBuilder extends RunnerBuilder { + @Override + public Runner runnerForClass(Class<?> each) throws Throwable { + return null; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java b/junit4/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java new file mode 100644 index 0000000..659bf31 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java @@ -0,0 +1,26 @@ +/** + * + */ +package org.junit.internal.builders; + +import org.junit.internal.runners.SuiteMethod; +import org.junit.runner.Runner; +import org.junit.runners.model.RunnerBuilder; + +public class SuiteMethodBuilder extends RunnerBuilder { + @Override + public Runner runnerForClass(Class<?> each) throws Throwable { + if (hasSuiteMethod(each)) + return new SuiteMethod(each); + return null; + } + + public boolean hasSuiteMethod(Class<?> testClass) { + try { + testClass.getMethod("suite"); + } catch (NoSuchMethodException e) { + return false; + } + return true; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/matchers/CombinableMatcher.java b/junit4/src/main/java/org/junit/internal/matchers/CombinableMatcher.java new file mode 100644 index 0000000..e9e6947 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/matchers/CombinableMatcher.java @@ -0,0 +1,34 @@ +package org.junit.internal.matchers; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.anyOf; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +public class CombinableMatcher<T> extends BaseMatcher<T> { + + private final Matcher<? extends T> fMatcher; + + public CombinableMatcher(Matcher<? extends T> matcher) { + fMatcher= matcher; + } + + public boolean matches(Object item) { + return fMatcher.matches(item); + } + + public void describeTo(Description description) { + description.appendDescriptionOf(fMatcher); + } + + @SuppressWarnings("unchecked") + public CombinableMatcher<T> and(Matcher<? extends T> matcher) { + return new CombinableMatcher<T>(allOf(matcher, fMatcher)); + } + + @SuppressWarnings("unchecked") + public CombinableMatcher<T> or(Matcher<? extends T> matcher) { + return new CombinableMatcher<T>(anyOf(matcher, fMatcher)); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/matchers/Each.java b/junit4/src/main/java/org/junit/internal/matchers/Each.java new file mode 100644 index 0000000..527db3b --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/matchers/Each.java @@ -0,0 +1,24 @@ +package org.junit.internal.matchers; + +import static org.hamcrest.CoreMatchers.not; +import static org.junit.internal.matchers.IsCollectionContaining.hasItem; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +public class Each { + public static <T> Matcher<Iterable<T>> each(final Matcher<T> individual) { + final Matcher<Iterable<T>> allItemsAre = not(hasItem(not(individual))); + + return new BaseMatcher<Iterable<T>>() { + public boolean matches(Object item) { + return allItemsAre.matches(item); + } + + public void describeTo(Description description) { + description.appendText("each "); + individual.describeTo(description); + } + }; + } +} diff --git a/junit4/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java b/junit4/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java new file mode 100644 index 0000000..4436a83 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java @@ -0,0 +1,67 @@ +package org.junit.internal.matchers; + +import static org.hamcrest.core.AllOf.allOf; +import static org.hamcrest.core.IsEqual.equalTo; + +import java.util.ArrayList; +import java.util.Collection; + +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; + +// Copied (hopefully temporarily) from hamcrest-library +public class IsCollectionContaining<T> extends TypeSafeMatcher<Iterable<T>> { + private final Matcher<? extends T> elementMatcher; + + public IsCollectionContaining(Matcher<? extends T> elementMatcher) { + this.elementMatcher = elementMatcher; + } + + @Override + public boolean matchesSafely(Iterable<T> collection) { + for (T item : collection) { + if (elementMatcher.matches(item)){ + return true; + } + } + return false; + } + + public void describeTo(Description description) { + description + .appendText("a collection containing ") + .appendDescriptionOf(elementMatcher); + } + + @Factory + public static <T> Matcher<Iterable<T>> hasItem(Matcher<? extends T> elementMatcher) { + return new IsCollectionContaining<T>(elementMatcher); + } + + @Factory + public static <T> Matcher<Iterable<T>> hasItem(T element) { + return hasItem(equalTo(element)); + } + + @Factory + public static <T> Matcher<Iterable<T>> hasItems(Matcher<? extends T>... elementMatchers) { + Collection<Matcher<? extends Iterable<T>>> all + = new ArrayList<Matcher<? extends Iterable<T>>>(elementMatchers.length); + for (Matcher<? extends T> elementMatcher : elementMatchers) { + all.add(hasItem(elementMatcher)); + } + return allOf(all); + } + + @Factory + public static <T> Matcher<Iterable<T>> hasItems(T... elements) { + Collection<Matcher<? extends Iterable<T>>> all + = new ArrayList<Matcher<? extends Iterable<T>>>(elements.length); + for (T element : elements) { + all.add(hasItem(element)); + } + return allOf(all); + } + +} diff --git a/junit4/src/main/java/org/junit/internal/matchers/StringContains.java b/junit4/src/main/java/org/junit/internal/matchers/StringContains.java new file mode 100644 index 0000000..e5f5334 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/matchers/StringContains.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2000-2006 hamcrest.org + */ +package org.junit.internal.matchers; + +import org.hamcrest.Factory; +import org.hamcrest.Matcher; + +/** + * Tests if the argument is a string that contains a substring. + */ +public class StringContains extends SubstringMatcher { + public StringContains(String substring) { + super(substring); + } + + @Override + protected boolean evalSubstringOf(String s) { + return s.indexOf(substring) >= 0; + } + + @Override + protected String relationship() { + return "containing"; + } + + @Factory + public static Matcher<String> containsString(String substring) { + return new StringContains(substring); + } + +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/matchers/SubstringMatcher.java b/junit4/src/main/java/org/junit/internal/matchers/SubstringMatcher.java new file mode 100644 index 0000000..1c65240 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/matchers/SubstringMatcher.java @@ -0,0 +1,28 @@ +package org.junit.internal.matchers; + +import org.hamcrest.Description; + +public abstract class SubstringMatcher extends TypeSafeMatcher<String> { + + protected final String substring; + + protected SubstringMatcher(final String substring) { + this.substring = substring; + } + + @Override + public boolean matchesSafely(String item) { + return evalSubstringOf(item); + } + + public void describeTo(Description description) { + description.appendText("a string ") + .appendText(relationship()) + .appendText(" ") + .appendValue(substring); + } + + protected abstract boolean evalSubstringOf(String string); + + protected abstract String relationship(); +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java b/junit4/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java new file mode 100644 index 0000000..794a174 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java @@ -0,0 +1,60 @@ +package org.junit.internal.matchers; + +import java.lang.reflect.Method; + +import org.hamcrest.BaseMatcher; + +/** + * Convenient base class for Matchers that require a non-null value of a specific type. + * This simply implements the null check, checks the type and then casts. + * + * @author Joe Walnes + */ +public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> { + + private Class<?> expectedType; + + /** + * Subclasses should implement this. The item will already have been checked for + * the specific type and will never be null. + */ + public abstract boolean matchesSafely(T item); + + protected TypeSafeMatcher() { + expectedType = findExpectedType(getClass()); + } + + private static Class<?> findExpectedType(Class<?> fromClass) { + for (Class<?> c = fromClass; c != Object.class; c = c.getSuperclass()) { + for (Method method : c.getDeclaredMethods()) { + if (isMatchesSafelyMethod(method)) { + return method.getParameterTypes()[0]; + } + } + } + + throw new Error("Cannot determine correct type for matchesSafely() method."); + } + + private static boolean isMatchesSafelyMethod(Method method) { + return method.getName().equals("matchesSafely") + && method.getParameterTypes().length == 1 + && !method.isSynthetic(); + } + + protected TypeSafeMatcher(Class<T> expectedType) { + this.expectedType = expectedType; + } + + /** + * Method made final to prevent accidental override. + * If you need to override this, there's no point on extending TypeSafeMatcher. + * Instead, extend the {@link BaseMatcher}. + */ + @SuppressWarnings({"unchecked"}) + public final boolean matches(Object item) { + return item != null + && expectedType.isInstance(item) + && matchesSafely((T) item); + } +} diff --git a/junit4/src/main/java/org/junit/internal/requests/ClassRequest.java b/junit4/src/main/java/org/junit/internal/requests/ClassRequest.java new file mode 100644 index 0000000..53bf520 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/requests/ClassRequest.java @@ -0,0 +1,26 @@ +package org.junit.internal.requests; + + +import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; +import org.junit.runner.Request; +import org.junit.runner.Runner; + +public class ClassRequest extends Request { + private final Class<?> fTestClass; + + private boolean fCanUseSuiteMethod; + + public ClassRequest(Class<?> testClass, boolean canUseSuiteMethod) { + fTestClass= testClass; + fCanUseSuiteMethod= canUseSuiteMethod; + } + + public ClassRequest(Class<?> testClass) { + this(testClass, true); + } + + @Override + public Runner getRunner() { + return new AllDefaultPossibilitiesBuilder(fCanUseSuiteMethod).safeRunnerForClass(fTestClass); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/requests/FilterRequest.java b/junit4/src/main/java/org/junit/internal/requests/FilterRequest.java new file mode 100644 index 0000000..e5d98d1 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/requests/FilterRequest.java @@ -0,0 +1,42 @@ +/** + * + */ +package org.junit.internal.requests; + +import org.junit.internal.runners.ErrorReportingRunner; +import org.junit.runner.Request; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.NoTestsRemainException; + +/** + * A filtered {@link Request}. + */ +public final class FilterRequest extends Request { + private final Request fRequest; + private final Filter fFilter; + + /** + * Creates a filtered Request + * @param classRequest a {@link Request} describing your Tests + * @param filter {@link Filter} to apply to the Tests described in + * <code>classRequest</code> + */ + public FilterRequest(Request classRequest, Filter filter) { + fRequest= classRequest; + fFilter= filter; + } + + @Override + public Runner getRunner() { + try { + Runner runner= fRequest.getRunner(); + fFilter.apply(runner); + return runner; + } catch (NoTestsRemainException e) { + return new ErrorReportingRunner(Filter.class, new Exception(String + .format("No tests found matching %s from %s", fFilter + .describe(), fRequest.toString()))); + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/requests/SortingRequest.java b/junit4/src/main/java/org/junit/internal/requests/SortingRequest.java new file mode 100644 index 0000000..3c6f4f5 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/requests/SortingRequest.java @@ -0,0 +1,25 @@ +package org.junit.internal.requests; + +import java.util.Comparator; + +import org.junit.runner.Description; +import org.junit.runner.Request; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Sorter; + +public class SortingRequest extends Request { + private final Request fRequest; + private final Comparator<Description> fComparator; + + public SortingRequest(Request request, Comparator<Description> comparator) { + fRequest= request; + fComparator= comparator; + } + + @Override + public Runner getRunner() { + Runner runner= fRequest.getRunner(); + new Sorter(fComparator).apply(runner); + return runner; + } +} diff --git a/junit4/src/main/java/org/junit/internal/requests/package-info.java b/junit4/src/main/java/org/junit/internal/requests/package-info.java new file mode 100644 index 0000000..66d2928 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/requests/package-info.java @@ -0,0 +1,6 @@ +/** + * Provides implementations of {@link org.junit.runner.Request}. + * + * @since 4.0 + */ +package org.junit.internal.requests;
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/ClassRoadie.java b/junit4/src/main/java/org/junit/internal/runners/ClassRoadie.java new file mode 100644 index 0000000..1f77d37 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/ClassRoadie.java @@ -0,0 +1,79 @@ +package org.junit.internal.runners; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * @deprecated Included for backwards compatibility with JUnit 4.4. Will be + * removed in the next release. Please use + * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. + */ +@Deprecated +public +class ClassRoadie { + private RunNotifier fNotifier; + private TestClass fTestClass; + private Description fDescription; + private final Runnable fRunnable; + + public ClassRoadie(RunNotifier notifier, TestClass testClass, + Description description, Runnable runnable) { + fNotifier= notifier; + fTestClass= testClass; + fDescription= description; + fRunnable= runnable; + } + + protected void runUnprotected() { + fRunnable.run(); + }; + + protected void addFailure(Throwable targetException) { + fNotifier.fireTestFailure(new Failure(fDescription, targetException)); + } + + public void runProtected() { + try { + runBefores(); + runUnprotected(); + } catch (FailedBefore e) { + } finally { + runAfters(); + } + } + + private void runBefores() throws FailedBefore { + try { + try { + List<Method> befores= fTestClass.getBefores(); + for (Method before : befores) + before.invoke(null); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } catch (org.junit.internal.AssumptionViolatedException e) { + throw new FailedBefore(); + } catch (Throwable e) { + addFailure(e); + throw new FailedBefore(); + } + } + + private void runAfters() { + List<Method> afters= fTestClass.getAfters(); + for (Method after : afters) + try { + after.invoke(null); + } catch (InvocationTargetException e) { + addFailure(e.getTargetException()); + } catch (Throwable e) { + addFailure(e); // Untested, but seems impossible + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java b/junit4/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java new file mode 100644 index 0000000..200b6f0 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java @@ -0,0 +1,60 @@ +package org.junit.internal.runners; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; + +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.InitializationError; + +public class ErrorReportingRunner extends Runner { + private final List<Throwable> fCauses; + + private final Class<?> fTestClass; + + public ErrorReportingRunner(Class<?> testClass, Throwable cause) { + fTestClass= testClass; + fCauses= getCauses(cause); + } + + @Override + public Description getDescription() { + Description description= Description.createSuiteDescription(fTestClass); + for (Throwable each : fCauses) + description.addChild(describeCause(each)); + return description; + } + + @Override + public void run(RunNotifier notifier) { + for (Throwable each : fCauses) + runCause(each, notifier); + } + + @SuppressWarnings("deprecation") + private List<Throwable> getCauses(Throwable cause) { + if (cause instanceof InvocationTargetException) + return getCauses(cause.getCause()); + if (cause instanceof InitializationError) + return ((InitializationError) cause).getCauses(); + if (cause instanceof org.junit.internal.runners.InitializationError) + return ((org.junit.internal.runners.InitializationError) cause) + .getCauses(); + return Arrays.asList(cause); + } + + private Description describeCause(Throwable child) { + return Description.createTestDescription(fTestClass, + "initializationError"); + } + + private void runCause(Throwable child, RunNotifier notifier) { + Description description= describeCause(child); + notifier.fireTestStarted(description); + notifier.fireTestFailure(new Failure(description, child)); + notifier.fireTestFinished(description); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/FailedBefore.java b/junit4/src/main/java/org/junit/internal/runners/FailedBefore.java new file mode 100644 index 0000000..29dcba4 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/FailedBefore.java @@ -0,0 +1,14 @@ +package org.junit.internal.runners; + +import org.junit.runners.BlockJUnit4ClassRunner; + + +/** + * @deprecated Included for backwards compatibility with JUnit 4.4. Will be + * removed in the next release. Please use + * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. + */ +@Deprecated +class FailedBefore extends Exception { + private static final long serialVersionUID= 1L; +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/InitializationError.java b/junit4/src/main/java/org/junit/internal/runners/InitializationError.java new file mode 100644 index 0000000..5715ec5 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/InitializationError.java @@ -0,0 +1,30 @@ +package org.junit.internal.runners; + +import java.util.Arrays; +import java.util.List; + +@Deprecated +/** + * Use the published version: {@link org.junit.runners.InitializationError} + * This may disappear as soon as 1 April 2009 + */ +public class InitializationError extends Exception { + private static final long serialVersionUID= 1L; + private final List<Throwable> fErrors; + + public InitializationError(List<Throwable> errors) { + fErrors= errors; + } + + public InitializationError(Throwable... errors) { + this(Arrays.asList(errors)); + } + + public InitializationError(String string) { + this(new Exception(string)); + } + + public List<Throwable> getCauses() { + return fErrors; + } +} diff --git a/junit4/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java b/junit4/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java new file mode 100644 index 0000000..0028d0c --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java @@ -0,0 +1,158 @@ +package org.junit.internal.runners; + +import junit.extensions.TestDecorator; +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestListener; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import org.junit.runner.Describable; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.manipulation.Sortable; +import org.junit.runner.manipulation.Sorter; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; + +public class JUnit38ClassRunner extends Runner implements Filterable, Sortable { + private final class OldTestClassAdaptingListener implements + TestListener { + private final RunNotifier fNotifier; + + private OldTestClassAdaptingListener(RunNotifier notifier) { + fNotifier= notifier; + } + + public void endTest(Test test) { + fNotifier.fireTestFinished(asDescription(test)); + } + + public void startTest(Test test) { + fNotifier.fireTestStarted(asDescription(test)); + } + + // Implement junit.framework.TestListener + public void addError(Test test, Throwable t) { + Failure failure= new Failure(asDescription(test), t); + fNotifier.fireTestFailure(failure); + } + + private Description asDescription(Test test) { + if (test instanceof Describable) { + Describable facade= (Describable) test; + return facade.getDescription(); + } + return Description.createTestDescription(getEffectiveClass(test), getName(test)); + } + + private Class<? extends Test> getEffectiveClass(Test test) { + return test.getClass(); + } + + private String getName(Test test) { + if (test instanceof TestCase) + return ((TestCase) test).getName(); + else + return test.toString(); + } + + public void addFailure(Test test, AssertionFailedError t) { + addError(test, t); + } + } + + private Test fTest; + + public JUnit38ClassRunner(Class<?> klass) { + this(new TestSuite(klass.asSubclass(TestCase.class))); + } + + public JUnit38ClassRunner(Test test) { + super(); + setTest(test); + } + + @Override + public void run(RunNotifier notifier) { + TestResult result= new TestResult(); + result.addListener(createAdaptingListener(notifier)); + getTest().run(result); + } + + public TestListener createAdaptingListener(final RunNotifier notifier) { + return new OldTestClassAdaptingListener(notifier); + } + + @Override + public Description getDescription() { + return makeDescription(getTest()); + } + + private static Description makeDescription(Test test) { + if (test instanceof TestCase) { + TestCase tc= (TestCase) test; + return Description.createTestDescription(tc.getClass(), tc.getName()); + } else if (test instanceof TestSuite) { + TestSuite ts= (TestSuite) test; + String name= ts.getName() == null ? createSuiteDescription(ts) : ts.getName(); + Description description= Description.createSuiteDescription(name); + int n= ts.testCount(); + for (int i= 0; i < n; i++) { + Description made= makeDescription(ts.testAt(i)); + description.addChild(made); + } + return description; + } else if (test instanceof Describable) { + Describable adapter= (Describable) test; + return adapter.getDescription(); + } else if (test instanceof TestDecorator) { + TestDecorator decorator= (TestDecorator) test; + return makeDescription(decorator.getTest()); + } else { + // This is the best we can do in this case + return Description.createSuiteDescription(test.getClass()); + } + } + + private static String createSuiteDescription(TestSuite ts) { + int count= ts.countTestCases(); + String example = count == 0 ? "" : String.format(" [example: %s]", ts.testAt(0)); + return String.format("TestSuite with %s tests%s", count, example); + } + + public void filter(Filter filter) throws NoTestsRemainException { + if (getTest() instanceof Filterable) { + Filterable adapter= (Filterable) getTest(); + adapter.filter(filter); + } else if (getTest() instanceof TestSuite) { + TestSuite suite= (TestSuite) getTest(); + TestSuite filtered= new TestSuite(suite.getName()); + int n= suite.testCount(); + for (int i= 0; i < n; i++) { + Test test= suite.testAt(i); + if (filter.shouldRun(makeDescription(test))) + filtered.addTest(test); + } + setTest(filtered); + } + } + + public void sort(Sorter sorter) { + if (getTest() instanceof Sortable) { + Sortable adapter= (Sortable) getTest(); + adapter.sort(sorter); + } + } + + private void setTest(Test test) { + fTest = test; + } + + private Test getTest() { + return fTest; + } +} diff --git a/junit4/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java b/junit4/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java new file mode 100644 index 0000000..d732880 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java @@ -0,0 +1,145 @@ +package org.junit.internal.runners; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.manipulation.Sortable; +import org.junit.runner.manipulation.Sorter; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * @deprecated Included for backwards compatibility with JUnit 4.4. Will be + * removed in the next release. Please use + * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. + * + * This may disappear as soon as 1 April 2009 + */ +@Deprecated +public class JUnit4ClassRunner extends Runner implements Filterable, Sortable { + private final List<Method> fTestMethods; + private TestClass fTestClass; + + public JUnit4ClassRunner(Class<?> klass) throws InitializationError { + fTestClass= new TestClass(klass); + fTestMethods= getTestMethods(); + validate(); + } + + protected List<Method> getTestMethods() { + return fTestClass.getTestMethods(); + } + + protected void validate() throws InitializationError { + MethodValidator methodValidator= new MethodValidator(fTestClass); + methodValidator.validateMethodsForDefaultRunner(); + methodValidator.assertValid(); + } + + @Override + public void run(final RunNotifier notifier) { + new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() { + public void run() { + runMethods(notifier); + } + }).runProtected(); + } + + protected void runMethods(final RunNotifier notifier) { + for (Method method : fTestMethods) + invokeTestMethod(method, notifier); + } + + @Override + public Description getDescription() { + Description spec= Description.createSuiteDescription(getName(), classAnnotations()); + List<Method> testMethods= fTestMethods; + for (Method method : testMethods) + spec.addChild(methodDescription(method)); + return spec; + } + + protected Annotation[] classAnnotations() { + return fTestClass.getJavaClass().getAnnotations(); + } + + protected String getName() { + return getTestClass().getName(); + } + + protected Object createTest() throws Exception { + return getTestClass().getConstructor().newInstance(); + } + + protected void invokeTestMethod(Method method, RunNotifier notifier) { + Description description= methodDescription(method); + Object test; + try { + test= createTest(); + } catch (InvocationTargetException e) { + testAborted(notifier, description, e.getCause()); + return; + } catch (Exception e) { + testAborted(notifier, description, e); + return; + } + TestMethod testMethod= wrapMethod(method); + new MethodRoadie(test, testMethod, notifier, description).run(); + } + + private void testAborted(RunNotifier notifier, Description description, + Throwable e) { + notifier.fireTestStarted(description); + notifier.fireTestFailure(new Failure(description, e)); + notifier.fireTestFinished(description); + } + + protected TestMethod wrapMethod(Method method) { + return new TestMethod(method, fTestClass); + } + + protected String testName(Method method) { + return method.getName(); + } + + protected Description methodDescription(Method method) { + return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method)); + } + + protected Annotation[] testAnnotations(Method method) { + return method.getAnnotations(); + } + + public void filter(Filter filter) throws NoTestsRemainException { + for (Iterator<Method> iter= fTestMethods.iterator(); iter.hasNext();) { + Method method= iter.next(); + if (!filter.shouldRun(methodDescription(method))) + iter.remove(); + } + if (fTestMethods.isEmpty()) + throw new NoTestsRemainException(); + } + + public void sort(final Sorter sorter) { + Collections.sort(fTestMethods, new Comparator<Method>() { + public int compare(Method o1, Method o2) { + return sorter.compare(methodDescription(o1), methodDescription(o2)); + } + }); + } + + protected TestClass getTestClass() { + return fTestClass; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/MethodRoadie.java b/junit4/src/main/java/org/junit/internal/runners/MethodRoadie.java new file mode 100644 index 0000000..4407821 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/MethodRoadie.java @@ -0,0 +1,157 @@ +package org.junit.internal.runners; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.internal.AssumptionViolatedException; +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * @deprecated Included for backwards compatibility with JUnit 4.4. Will be + * removed in the next release. Please use + * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. + */ +@Deprecated +public class MethodRoadie { + private final Object fTest; + private final RunNotifier fNotifier; + private final Description fDescription; + private TestMethod fTestMethod; + + public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) { + fTest= test; + fNotifier= notifier; + fDescription= description; + fTestMethod= method; + } + + public void run() { + if (fTestMethod.isIgnored()) { + fNotifier.fireTestIgnored(fDescription); + return; + } + fNotifier.fireTestStarted(fDescription); + try { + long timeout= fTestMethod.getTimeout(); + if (timeout > 0) + runWithTimeout(timeout); + else + runTest(); + } finally { + fNotifier.fireTestFinished(fDescription); + } + } + + private void runWithTimeout(final long timeout) { + runBeforesThenTestThenAfters(new Runnable() { + + public void run() { + ExecutorService service= Executors.newSingleThreadExecutor(); + Callable<Object> callable= new Callable<Object>() { + public Object call() throws Exception { + runTestMethod(); + return null; + } + }; + Future<Object> result= service.submit(callable); + service.shutdown(); + try { + boolean terminated= service.awaitTermination(timeout, + TimeUnit.MILLISECONDS); + if (!terminated) + service.shutdownNow(); + result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation + } catch (TimeoutException e) { + addFailure(new Exception(String.format("test timed out after %d milliseconds", timeout))); + } catch (Exception e) { + addFailure(e); + } + } + }); + } + + public void runTest() { + runBeforesThenTestThenAfters(new Runnable() { + public void run() { + runTestMethod(); + } + }); + } + + public void runBeforesThenTestThenAfters(Runnable test) { + try { + runBefores(); + test.run(); + } catch (FailedBefore e) { + } catch (Exception e) { + throw new RuntimeException("test should never throw an exception to this level"); + } finally { + runAfters(); + } + } + + protected void runTestMethod() { + try { + fTestMethod.invoke(fTest); + if (fTestMethod.expectsException()) + addFailure(new AssertionError("Expected exception: " + fTestMethod.getExpectedException().getName())); + } catch (InvocationTargetException e) { + Throwable actual= e.getTargetException(); + if (actual instanceof AssumptionViolatedException) + return; + else if (!fTestMethod.expectsException()) + addFailure(actual); + else if (fTestMethod.isUnexpected(actual)) { + String message= "Unexpected exception, expected<" + fTestMethod.getExpectedException().getName() + "> but was<" + + actual.getClass().getName() + ">"; + addFailure(new Exception(message, actual)); + } + } catch (Throwable e) { + addFailure(e); + } + } + + private void runBefores() throws FailedBefore { + try { + try { + List<Method> befores= fTestMethod.getBefores(); + for (Method before : befores) + before.invoke(fTest); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } catch (AssumptionViolatedException e) { + throw new FailedBefore(); + } catch (Throwable e) { + addFailure(e); + throw new FailedBefore(); + } + } + + private void runAfters() { + List<Method> afters= fTestMethod.getAfters(); + for (Method after : afters) + try { + after.invoke(fTest); + } catch (InvocationTargetException e) { + addFailure(e.getTargetException()); + } catch (Throwable e) { + addFailure(e); // Untested, but seems impossible + } + } + + protected void addFailure(Throwable e) { + fNotifier.fireTestFailure(new Failure(fDescription, e)); + } +} + diff --git a/junit4/src/main/java/org/junit/internal/runners/MethodValidator.java b/junit4/src/main/java/org/junit/internal/runners/MethodValidator.java new file mode 100644 index 0000000..cadc93f --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/MethodValidator.java @@ -0,0 +1,91 @@ +package org.junit.internal.runners; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * @deprecated Included for backwards compatibility with JUnit 4.4. Will be + * removed in the next release. Please use + * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. + */ +@Deprecated +public class MethodValidator { + + private final List<Throwable> fErrors= new ArrayList<Throwable>(); + + private TestClass fTestClass; + + public MethodValidator(TestClass testClass) { + fTestClass = testClass; + } + + public void validateInstanceMethods() { + validateTestMethods(After.class, false); + validateTestMethods(Before.class, false); + validateTestMethods(Test.class, false); + + List<Method> methods= fTestClass.getAnnotatedMethods(Test.class); + if (methods.size() == 0) + fErrors.add(new Exception("No runnable methods")); + } + + public void validateStaticMethods() { + validateTestMethods(BeforeClass.class, true); + validateTestMethods(AfterClass.class, true); + } + + public List<Throwable> validateMethodsForDefaultRunner() { + validateNoArgConstructor(); + validateStaticMethods(); + validateInstanceMethods(); + return fErrors; + } + + public void assertValid() throws InitializationError { + if (!fErrors.isEmpty()) + throw new InitializationError(fErrors); + } + + public void validateNoArgConstructor() { + try { + fTestClass.getConstructor(); + } catch (Exception e) { + fErrors.add(new Exception("Test class should have public zero-argument constructor", e)); + } + } + + private void validateTestMethods(Class<? extends Annotation> annotation, + boolean isStatic) { + List<Method> methods= fTestClass.getAnnotatedMethods(annotation); + + for (Method each : methods) { + if (Modifier.isStatic(each.getModifiers()) != isStatic) { + String state= isStatic ? "should" : "should not"; + fErrors.add(new Exception("Method " + each.getName() + "() " + + state + " be static")); + } + if (!Modifier.isPublic(each.getDeclaringClass().getModifiers())) + fErrors.add(new Exception("Class " + each.getDeclaringClass().getName() + + " should be public")); + if (!Modifier.isPublic(each.getModifiers())) + fErrors.add(new Exception("Method " + each.getName() + + " should be public")); + if (each.getReturnType() != Void.TYPE) + fErrors.add(new Exception("Method " + each.getName() + + " should be void")); + if (each.getParameterTypes().length != 0) + fErrors.add(new Exception("Method " + each.getName() + + " should have no parameters")); + } + } +} diff --git a/junit4/src/main/java/org/junit/internal/runners/SuiteMethod.java b/junit4/src/main/java/org/junit/internal/runners/SuiteMethod.java new file mode 100644 index 0000000..4e8bebc --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/SuiteMethod.java @@ -0,0 +1,40 @@ +package org.junit.internal.runners; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import junit.framework.Test; + +/** Runner for use with JUnit 3.8.x-style AllTests classes + * (those that only implement a static <code>suite()</code> + * method). For example: + * <pre> + * @RunWith(AllTests.class) + * public class ProductTests { + * public static junit.framework.Test suite() { + * ... + * } + * } + * </pre> + */ +public class SuiteMethod extends JUnit38ClassRunner { + public SuiteMethod(Class<?> klass) throws Throwable { + super(testFromSuiteMethod(klass)); + } + + public static Test testFromSuiteMethod(Class<?> klass) throws Throwable { + Method suiteMethod= null; + Test suite= null; + try { + suiteMethod= klass.getMethod("suite"); + if (! Modifier.isStatic(suiteMethod.getModifiers())) { + throw new Exception(klass.getName() + ".suite() must be static"); + } + suite= (Test) suiteMethod.invoke(null); // static method + } catch (InvocationTargetException e) { + throw e.getCause(); + } + return suite; + } +} diff --git a/junit4/src/main/java/org/junit/internal/runners/TestClass.java b/junit4/src/main/java/org/junit/internal/runners/TestClass.java new file mode 100644 index 0000000..1ca2b9d --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/TestClass.java @@ -0,0 +1,102 @@ +package org.junit.internal.runners; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * @deprecated Included for backwards compatibility with JUnit 4.4. Will be + * removed in the next release. Please use + * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. + */ +@Deprecated +public class TestClass { + private final Class<?> fClass; + + public TestClass(Class<?> klass) { + fClass= klass; + } + + public List<Method> getTestMethods() { + return getAnnotatedMethods(Test.class); + } + + List<Method> getBefores() { + return getAnnotatedMethods(BeforeClass.class); + } + + List<Method> getAfters() { + return getAnnotatedMethods(AfterClass.class); + } + + public List<Method> getAnnotatedMethods(Class<? extends Annotation> annotationClass) { + List<Method> results= new ArrayList<Method>(); + for (Class<?> eachClass : getSuperClasses(fClass)) { + Method[] methods= eachClass.getDeclaredMethods(); + for (Method eachMethod : methods) { + Annotation annotation= eachMethod.getAnnotation(annotationClass); + if (annotation != null && ! isShadowed(eachMethod, results)) + results.add(eachMethod); + } + } + if (runsTopToBottom(annotationClass)) + Collections.reverse(results); + return results; + } + + private boolean runsTopToBottom(Class< ? extends Annotation> annotation) { + return annotation.equals(Before.class) || annotation.equals(BeforeClass.class); + } + + private boolean isShadowed(Method method, List<Method> results) { + for (Method each : results) { + if (isShadowed(method, each)) + return true; + } + return false; + } + + private boolean isShadowed(Method current, Method previous) { + if (! previous.getName().equals(current.getName())) + return false; + if (previous.getParameterTypes().length != current.getParameterTypes().length) + return false; + for (int i= 0; i < previous.getParameterTypes().length; i++) { + if (! previous.getParameterTypes()[i].equals(current.getParameterTypes()[i])) + return false; + } + return true; + } + + private List<Class<?>> getSuperClasses(Class< ?> testClass) { + ArrayList<Class<?>> results= new ArrayList<Class<?>>(); + Class<?> current= testClass; + while (current != null) { + results.add(current); + current= current.getSuperclass(); + } + return results; + } + + public Constructor<?> getConstructor() throws SecurityException, NoSuchMethodException { + return fClass.getConstructor(); + } + + public Class<?> getJavaClass() { + return fClass; + } + + public String getName() { + return fClass.getName(); + } + +} diff --git a/junit4/src/main/java/org/junit/internal/runners/TestMethod.java b/junit4/src/main/java/org/junit/internal/runners/TestMethod.java new file mode 100644 index 0000000..a06213c --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/TestMethod.java @@ -0,0 +1,69 @@ +package org.junit.internal.runners; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.Test.None; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * @deprecated Included for backwards compatibility with JUnit 4.4. Will be + * removed in the next release. Please use + * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. + */ +@Deprecated +public class TestMethod { + private final Method fMethod; + private TestClass fTestClass; + + public TestMethod(Method method, TestClass testClass) { + fMethod= method; + fTestClass= testClass; + } + + public boolean isIgnored() { + return fMethod.getAnnotation(Ignore.class) != null; + } + + public long getTimeout() { + Test annotation= fMethod.getAnnotation(Test.class); + if (annotation == null) + return 0; + long timeout= annotation.timeout(); + return timeout; + } + + protected Class<? extends Throwable> getExpectedException() { + Test annotation= fMethod.getAnnotation(Test.class); + if (annotation == null || annotation.expected() == None.class) + return null; + else + return annotation.expected(); + } + + boolean isUnexpected(Throwable exception) { + return ! getExpectedException().isAssignableFrom(exception.getClass()); + } + + boolean expectsException() { + return getExpectedException() != null; + } + + List<Method> getBefores() { + return fTestClass.getAnnotatedMethods(Before.class); + } + + List<Method> getAfters() { + return fTestClass.getAnnotatedMethods(After.class); + } + + public void invoke(Object test) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + fMethod.invoke(test); + } + +} diff --git a/junit4/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java b/junit4/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java new file mode 100644 index 0000000..a7d534c --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java @@ -0,0 +1,51 @@ +/** + * + */ +package org.junit.internal.runners.model; + +import org.junit.internal.AssumptionViolatedException; +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.MultipleFailureException; + +public class EachTestNotifier { + private final RunNotifier fNotifier; + + private final Description fDescription; + + public EachTestNotifier(RunNotifier notifier, Description description) { + fNotifier= notifier; + fDescription= description; + } + + public void addFailure(Throwable targetException) { + if (targetException instanceof MultipleFailureException) { + addMultipleFailureException((MultipleFailureException) targetException); + } else { + fNotifier + .fireTestFailure(new Failure(fDescription, targetException)); + } + } + + private void addMultipleFailureException(MultipleFailureException mfe) { + for (Throwable each : mfe.getFailures()) + addFailure(each); + } + + public void addFailedAssumption(AssumptionViolatedException e) { + fNotifier.fireTestAssumptionFailed(new Failure(fDescription, e)); + } + + public void fireTestFinished() { + fNotifier.fireTestFinished(fDescription); + } + + public void fireTestStarted() { + fNotifier.fireTestStarted(fDescription); + } + + public void fireTestIgnored() { + fNotifier.fireTestIgnored(fDescription); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java b/junit4/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java new file mode 100644 index 0000000..3316806 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java @@ -0,0 +1,12 @@ +package org.junit.internal.runners.model; + +import java.util.List; + +@Deprecated +public class MultipleFailureException extends org.junit.runners.model.MultipleFailureException { + private static final long serialVersionUID= 1L; + + public MultipleFailureException(List<Throwable> errors) { + super(errors); + } +} diff --git a/junit4/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java b/junit4/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java new file mode 100644 index 0000000..9150d90 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java @@ -0,0 +1,22 @@ +/** + * + */ +package org.junit.internal.runners.model; + +import java.lang.reflect.InvocationTargetException; + +/** + * When invoked, throws the exception from the reflected method, rather than + * wrapping it in an InvocationTargetException. + */ +public abstract class ReflectiveCallable { + public Object run() throws Throwable { + try { + return runReflectiveCall(); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + + protected abstract Object runReflectiveCall() throws Throwable; +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/package-info.java b/junit4/src/main/java/org/junit/internal/runners/package-info.java new file mode 100644 index 0000000..5ab7e7b --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/package-info.java @@ -0,0 +1,6 @@ +/** + * Provides implementations of {@link org.junit.runner.Runner} + * + * @since 4.0 + */ +package org.junit.internal.runners;
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java b/junit4/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java new file mode 100644 index 0000000..e7df8bf --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java @@ -0,0 +1,92 @@ +package org.junit.internal.runners.rules; + +import java.lang.annotation.Annotation; +import java.util.List; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.runners.model.FrameworkField; +import org.junit.runners.model.TestClass; + +/** + * A RuleFieldValidator validates the rule fields of a + * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the + * {@code TestClass} are written to a list of errors. + * + * There are two slightly different validators. The {@link #CLASS_RULE_VALIDATOR} + * validates fields with a {@link ClassRule} annotation and the + * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation. + */ +public enum RuleFieldValidator { + /** + * Validates fields with a {@link ClassRule} annotation. + */ + CLASS_RULE_VALIDATOR(ClassRule.class, true), + /** + * Validates fields with a {@link Rule} annotation. + */ + RULE_VALIDATOR(Rule.class, false); + + private final Class<? extends Annotation> fAnnotation; + + private final boolean fOnlyStaticFields; + + private RuleFieldValidator(Class<? extends Annotation> annotation, + boolean onlyStaticFields) { + this.fAnnotation= annotation; + this.fOnlyStaticFields= onlyStaticFields; + } + + /** + * Validate the {@link org.junit.runners.model.TestClass} and adds reasons + * for rejecting the class to a list of errors. + * @param target the {@code TestClass} to validate. + * @param errors the list of errors. + */ + public void validate(TestClass target, List<Throwable> errors) { + List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation); + for (FrameworkField each : fields) + validateField(each, errors); + } + + private void validateField(FrameworkField field, List<Throwable> errors) { + optionallyValidateStatic(field, errors); + validatePublic(field, errors); + validateTestRuleOrMethodRule(field, errors); + } + + private void optionallyValidateStatic(FrameworkField field, + List<Throwable> errors) { + if (fOnlyStaticFields && !field.isStatic()) + addError(errors, field, "must be static."); + } + + private void validatePublic(FrameworkField field, List<Throwable> errors) { + if (!field.isPublic()) + addError(errors, field, "must be public."); + } + + private void validateTestRuleOrMethodRule(FrameworkField field, + List<Throwable> errors) { + if (!isMethodRule(field) && !isTestRule(field)) + addError(errors, field, "must implement MethodRule or TestRule."); + } + + private boolean isTestRule(FrameworkField target) { + return TestRule.class.isAssignableFrom(target.getType()); + } + + @SuppressWarnings("deprecation") + private boolean isMethodRule(FrameworkField target) { + return org.junit.rules.MethodRule.class.isAssignableFrom(target + .getType()); + } + + private void addError(List<Throwable> errors, FrameworkField field, + String suffix) { + String message= "The @" + fAnnotation.getSimpleName() + " '" + + field.getName() + "' " + suffix; + errors.add(new Exception(message)); + } +} diff --git a/junit4/src/main/java/org/junit/internal/runners/statements/ExpectException.java b/junit4/src/main/java/org/junit/internal/runners/statements/ExpectException.java new file mode 100644 index 0000000..ddfef07 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/statements/ExpectException.java @@ -0,0 +1,38 @@ +/** + * + */ +package org.junit.internal.runners.statements; + +import org.junit.internal.AssumptionViolatedException; +import org.junit.runners.model.Statement; + +public class ExpectException extends Statement { + private Statement fNext; + private final Class<? extends Throwable> fExpected; + + public ExpectException(Statement next, Class<? extends Throwable> expected) { + fNext= next; + fExpected= expected; + } + + @Override + public void evaluate() throws Exception { + boolean complete = false; + try { + fNext.evaluate(); + complete = true; + } catch (AssumptionViolatedException e) { + throw e; + } catch (Throwable e) { + if (!fExpected.isAssignableFrom(e.getClass())) { + String message= "Unexpected exception, expected<" + + fExpected.getName() + "> but was<" + + e.getClass().getName() + ">"; + throw new Exception(message, e); + } + } + if (complete) + throw new AssertionError("Expected exception: " + + fExpected.getName()); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/statements/Fail.java b/junit4/src/main/java/org/junit/internal/runners/statements/Fail.java new file mode 100644 index 0000000..e7d0d5c --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/statements/Fail.java @@ -0,0 +1,17 @@ +package org.junit.internal.runners.statements; + +import org.junit.runners.model.Statement; + + +public class Fail extends Statement { + private final Throwable fError; + + public Fail(Throwable e) { + fError= e; + } + + @Override + public void evaluate() throws Throwable { + throw fError; + } +} diff --git a/junit4/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java b/junit4/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java new file mode 100644 index 0000000..bff7c72 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java @@ -0,0 +1,71 @@ +/** + * + */ +package org.junit.internal.runners.statements; + +import org.junit.runners.model.Statement; + +public class FailOnTimeout extends Statement { + private final Statement fOriginalStatement; + + private final long fTimeout; + + public FailOnTimeout(Statement originalStatement, long timeout) { + fOriginalStatement= originalStatement; + fTimeout= timeout; + } + + @Override + public void evaluate() throws Throwable { + StatementThread thread= evaluateStatement(); + if (!thread.fFinished) + throwExceptionForUnfinishedThread(thread); + } + + private StatementThread evaluateStatement() throws InterruptedException { + StatementThread thread= new StatementThread(fOriginalStatement); + thread.start(); + thread.join(fTimeout); + thread.interrupt(); + return thread; + } + + private void throwExceptionForUnfinishedThread(StatementThread thread) + throws Throwable { + if (thread.fExceptionThrownByOriginalStatement != null) + throw thread.fExceptionThrownByOriginalStatement; + else + throwTimeoutException(thread); + } + + private void throwTimeoutException(StatementThread thread) throws Exception { + Exception exception= new Exception(String.format( + "test timed out after %d milliseconds", fTimeout)); + exception.setStackTrace(thread.getStackTrace()); + throw exception; + } + + private static class StatementThread extends Thread { + private final Statement fStatement; + + private boolean fFinished= false; + + private Throwable fExceptionThrownByOriginalStatement= null; + + public StatementThread(Statement statement) { + fStatement= statement; + } + + @Override + public void run() { + try { + fStatement.evaluate(); + fFinished= true; + } catch (InterruptedException e) { + //don't log the InterruptedException + } catch (Throwable e) { + fExceptionThrownByOriginalStatement= e; + } + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java b/junit4/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java new file mode 100644 index 0000000..e2e81e1 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java @@ -0,0 +1,22 @@ +/** + * + */ +package org.junit.internal.runners.statements; + +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +public class InvokeMethod extends Statement { + private final FrameworkMethod fTestMethod; + private Object fTarget; + + public InvokeMethod(FrameworkMethod testMethod, Object target) { + fTestMethod= testMethod; + fTarget= target; + } + + @Override + public void evaluate() throws Throwable { + fTestMethod.invokeExplosively(fTarget); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/statements/RunAfters.java b/junit4/src/main/java/org/junit/internal/runners/statements/RunAfters.java new file mode 100644 index 0000000..475ec72 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/statements/RunAfters.java @@ -0,0 +1,43 @@ +/** + * + */ +package org.junit.internal.runners.statements; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.MultipleFailureException; +import org.junit.runners.model.Statement; + +public class RunAfters extends Statement { + private final Statement fNext; + + private final Object fTarget; + + private final List<FrameworkMethod> fAfters; + + public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) { + fNext= next; + fAfters= afters; + fTarget= target; + } + + @Override + public void evaluate() throws Throwable { + List<Throwable> errors = new ArrayList<Throwable>(); + try { + fNext.evaluate(); + } catch (Throwable e) { + errors.add(e); + } finally { + for (FrameworkMethod each : fAfters) + try { + each.invokeExplosively(fTarget); + } catch (Throwable e) { + errors.add(e); + } + } + MultipleFailureException.assertEmpty(errors); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/internal/runners/statements/RunBefores.java b/junit4/src/main/java/org/junit/internal/runners/statements/RunBefores.java new file mode 100644 index 0000000..66a34e1 --- /dev/null +++ b/junit4/src/main/java/org/junit/internal/runners/statements/RunBefores.java @@ -0,0 +1,30 @@ +/** + * + */ +package org.junit.internal.runners.statements; + +import java.util.List; + +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +public class RunBefores extends Statement { + private final Statement fNext; + + private final Object fTarget; + + private final List<FrameworkMethod> fBefores; + + public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) { + fNext= next; + fBefores= befores; + fTarget= target; + } + + @Override + public void evaluate() throws Throwable { + for (FrameworkMethod before : fBefores) + before.invokeExplosively(fTarget); + fNext.evaluate(); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/matchers/JUnitMatchers.java b/junit4/src/main/java/org/junit/matchers/JUnitMatchers.java new file mode 100644 index 0000000..ec2ec4a --- /dev/null +++ b/junit4/src/main/java/org/junit/matchers/JUnitMatchers.java @@ -0,0 +1,83 @@ +package org.junit.matchers; + +import org.hamcrest.Matcher; +import org.junit.internal.matchers.CombinableMatcher; +import org.junit.internal.matchers.Each; +import org.junit.internal.matchers.IsCollectionContaining; +import org.junit.internal.matchers.StringContains; + +/** + * Convenience import class: these are useful matchers for use with the assertThat method, but they are + * not currently included in the basic CoreMatchers class from hamcrest. + */ +public class JUnitMatchers { + /** + * @param element + * @return A matcher matching any collection containing element + */ + public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItem(T element) { + return IsCollectionContaining.hasItem(element); + } + + /** + * @param elementMatcher + * @return A matcher matching any collection containing an element matching elementMatcher + */ + public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItem(org.hamcrest.Matcher<? extends T> elementMatcher) { + return IsCollectionContaining.hasItem(elementMatcher); + } + + /** + * @param elements + * @return A matcher matching any collection containing every element in elements + */ + public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItems(T... elements) { + return IsCollectionContaining.hasItems(elements); + } + + /** + * @param elementMatchers + * @return A matcher matching any collection containing at least one element that matches + * each matcher in elementMatcher (this may be one element matching all matchers, + * or different elements matching each matcher) + */ + public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItems(org.hamcrest.Matcher<? extends T>... elementMatchers) { + return IsCollectionContaining.hasItems(elementMatchers); + } + + /** + * @param elementMatcher + * @return A matcher matching any collection in which every element matches elementMatcher + */ + public static <T> Matcher<Iterable<T>> everyItem(final Matcher<T> elementMatcher) { + return Each.each(elementMatcher); + } + + /** + * @param substring + * @return a matcher matching any string that contains substring + */ + public static org.hamcrest.Matcher<java.lang.String> containsString(java.lang.String substring) { + return StringContains.containsString(substring); + } + + /** + * This is useful for fluently combining matchers that must both pass. For example: + * <pre> + * assertThat(string, both(containsString("a")).and(containsString("b"))); + * </pre> + */ + public static <T> CombinableMatcher<T> both(Matcher<T> matcher) { + return new CombinableMatcher<T>(matcher); + } + + /** + * This is useful for fluently combining matchers where either may pass, for example: + * <pre> + * assertThat(string, either(containsString("a")).or(containsString("b"))); + * </pre> + */ + public static <T> CombinableMatcher<T> either(Matcher<T> matcher) { + return new CombinableMatcher<T>(matcher); + } +} diff --git a/junit4/src/main/java/org/junit/matchers/package-info.java b/junit4/src/main/java/org/junit/matchers/package-info.java new file mode 100644 index 0000000..71aca34 --- /dev/null +++ b/junit4/src/main/java/org/junit/matchers/package-info.java @@ -0,0 +1,9 @@ +/** + * Provides useful additional {@link org.hamcrest.Matcher}s for use with + * the {@link org.junit.Assert#assertThat(Object, org.hamcrest.Matcher)} + * statement + * + * @since 4.0 + * @see org.junit.matchers.JUnitMatchers + */ +package org.junit.matchers;
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/package-info.java b/junit4/src/main/java/org/junit/package-info.java new file mode 100644 index 0000000..bb60d0d --- /dev/null +++ b/junit4/src/main/java/org/junit/package-info.java @@ -0,0 +1,8 @@ +/** + * Provides JUnit core classes and annotations. + * + * Corresponds to junit.framework in Junit 3.x. + * + * @since 4.0 + */ +package org.junit;
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/rules/ErrorCollector.java b/junit4/src/main/java/org/junit/rules/ErrorCollector.java new file mode 100644 index 0000000..3522a65 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/ErrorCollector.java @@ -0,0 +1,85 @@ +/** + * + */ +package org.junit.rules; + +import static org.junit.Assert.assertThat; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +import org.hamcrest.Matcher; +import org.junit.runners.model.MultipleFailureException; + +/** + * The ErrorCollector rule allows execution of a test to continue after the + * first problem is found (for example, to collect _all_ the incorrect rows in a + * table, and report them all at once): + * + * <pre> + * public static class UsesErrorCollectorTwice { + * @Rule + * public ErrorCollector collector= new ErrorCollector(); + * + * @Test + * public void example() { + * collector.addError(new Throwable("first thing went wrong")); + * collector.addError(new Throwable("second thing went wrong")); + * collector.checkThat(getResult(), not(containsString("ERROR!"))); + * // all lines will run, and then a combined failure logged at the end. + * } + * } + * </pre> + */ +public class ErrorCollector extends Verifier { + private List<Throwable> errors= new ArrayList<Throwable>(); + + @Override + protected void verify() throws Throwable { + MultipleFailureException.assertEmpty(errors); + } + + /** + * Adds a Throwable to the table. Execution continues, but the test will fail at the end. + */ + public void addError(Throwable error) { + errors.add(error); + } + + /** + * Adds a failure to the table if {@code matcher} does not match {@code value}. + * Execution continues, but the test will fail at the end if the match fails. + */ + public <T> void checkThat(final T value, final Matcher<T> matcher) { + checkThat("", value, matcher); + } + + /** + * Adds a failure with the given {@code reason} + * to the table if {@code matcher} does not match {@code value}. + * Execution continues, but the test will fail at the end if the match fails. + */ + public <T> void checkThat(final String reason, final T value, final Matcher<T> matcher) { + checkSucceeds(new Callable<Object>() { + public Object call() throws Exception { + assertThat(reason, value, matcher); + return value; + } + }); + } + + /** + * Adds to the table the exception, if any, thrown from {@code callable}. + * Execution continues, but the test will fail at the end if + * {@code callable} threw an exception. + */ + public Object checkSucceeds(Callable<Object> callable) { + try { + return callable.call(); + } catch (Throwable e) { + addError(e); + return null; + } + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/rules/ExpectedException.java b/junit4/src/main/java/org/junit/rules/ExpectedException.java new file mode 100644 index 0000000..bac2fba --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/ExpectedException.java @@ -0,0 +1,136 @@ +package org.junit.rules; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.matchers.JUnitMatchers.both; +import static org.junit.matchers.JUnitMatchers.containsString; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.junit.Assert; +import org.junit.internal.matchers.TypeSafeMatcher; +import org.junit.runners.model.Statement; + +/** + * The ExpectedException Rule allows in-test specification of expected exception + * types and messages: + * + * <pre> + * // These tests all pass. + * public static class HasExpectedException { + * @Rule + * public ExpectedException thrown= ExpectedException.none(); + * + * @Test + * public void throwsNothing() { + * // no exception expected, none thrown: passes. + * } + * + * @Test + * public void throwsNullPointerException() { + * thrown.expect(NullPointerException.class); + * throw new NullPointerException(); + * } + * + * @Test + * public void throwsNullPointerExceptionWithMessage() { + * thrown.expect(NullPointerException.class); + * thrown.expectMessage("happened?"); + * thrown.expectMessage(startsWith("What")); + * throw new NullPointerException("What happened?"); + * } + * } + * </pre> + */ +public class ExpectedException implements TestRule { + /** + * @return a Rule that expects no exception to be thrown + * (identical to behavior without this Rule) + */ + public static ExpectedException none() { + return new ExpectedException(); + } + + private Matcher<Object> fMatcher= null; + + private ExpectedException() { + + } + + public Statement apply(Statement base, + org.junit.runner.Description description) { + return new ExpectedExceptionStatement(base); + } + + /** + * Adds {@code matcher} to the list of requirements for any thrown exception. + */ + // Should be able to remove this suppression in some brave new hamcrest world. + @SuppressWarnings("unchecked") + public void expect(Matcher<?> matcher) { + if (fMatcher == null) + fMatcher= (Matcher<Object>) matcher; + else + fMatcher= both(fMatcher).and(matcher); + } + + /** + * Adds to the list of requirements for any thrown exception that it + * should be an instance of {@code type} + */ + public void expect(Class<? extends Throwable> type) { + expect(instanceOf(type)); + } + + /** + * Adds to the list of requirements for any thrown exception that it + * should <em>contain</em> string {@code substring} + */ + public void expectMessage(String substring) { + expectMessage(containsString(substring)); + } + + /** + * Adds {@code matcher} to the list of requirements for the message + * returned from any thrown exception. + */ + public void expectMessage(Matcher<String> matcher) { + expect(hasMessage(matcher)); + } + + private class ExpectedExceptionStatement extends Statement { + private final Statement fNext; + + public ExpectedExceptionStatement(Statement base) { + fNext= base; + } + + @Override + public void evaluate() throws Throwable { + try { + fNext.evaluate(); + } catch (Throwable e) { + if (fMatcher == null) + throw e; + Assert.assertThat(e, fMatcher); + return; + } + if (fMatcher != null) + throw new AssertionError("Expected test to throw " + + StringDescription.toString(fMatcher)); + } + } + + private Matcher<Throwable> hasMessage(final Matcher<String> matcher) { + return new TypeSafeMatcher<Throwable>() { + public void describeTo(Description description) { + description.appendText("exception with message "); + description.appendDescriptionOf(matcher); + } + + @Override + public boolean matchesSafely(Throwable item) { + return matcher.matches(item.getMessage()); + } + }; + } +} diff --git a/junit4/src/main/java/org/junit/rules/ExternalResource.java b/junit4/src/main/java/org/junit/rules/ExternalResource.java new file mode 100644 index 0000000..1fe3719 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/ExternalResource.java @@ -0,0 +1,68 @@ +package org.junit.rules; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * A base class for Rules (like TemporaryFolder) that set up an external + * resource before a test (a file, socket, server, database connection, etc.), + * and guarantee to tear it down afterward: + * + * <pre> + * public static class UsesExternalResource { + * Server myServer= new Server(); + * + * @Rule + * public ExternalResource resource= new ExternalResource() { + * @Override + * protected void before() throws Throwable { + * myServer.connect(); + * }; + * + * @Override + * protected void after() { + * myServer.disconnect(); + * }; + * }; + * + * @Test + * public void testFoo() { + * new Client().run(myServer); + * } + * } + * </pre> + */ +public abstract class ExternalResource implements TestRule { + public Statement apply(Statement base, Description description) { + return statement(base); + } + + private Statement statement(final Statement base) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + before(); + try { + base.evaluate(); + } finally { + after(); + } + } + }; + } + + /** + * Override to set up your specific external resource. + * @throws if setup fails (which will disable {@code after} + */ + protected void before() throws Throwable { + // do nothing + } + + /** + * Override to tear down your specific external resource. + */ + protected void after() { + // do nothing + } +} diff --git a/junit4/src/main/java/org/junit/rules/MethodRule.java b/junit4/src/main/java/org/junit/rules/MethodRule.java new file mode 100644 index 0000000..5167672 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/MethodRule.java @@ -0,0 +1,40 @@ +package org.junit.rules; + +import org.junit.Rule; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +/** + * A MethodRule is an alteration in how a test method is run and reported. + * Multiple {@link MethodRule}s can be applied to a test method. The + * {@link Statement} that executes the method is passed to each annotated + * {@link Rule} in turn, and each may return a substitute or modified + * {@link Statement}, which is passed to the next {@link Rule}, if any. For + * examples of how this can be useful, see these provided MethodRules, + * or write your own: + * + * <ul> + * <li>{@link ErrorCollector}: collect multiple errors in one test method</li> + * <li>{@link ExpectedException}: make flexible assertions about thrown exceptions</li> + * <li>{@link ExternalResource}: start and stop a server, for example</li> + * <li>{@link TemporaryFolder}: create fresh files, and delete after test</li> + * <li>{@link TestName}: remember the test name for use during the method</li> + * <li>{@link TestWatchman}: add logic at events during method execution</li> + * <li>{@link Timeout}: cause test to fail after a set time</li> + * <li>{@link Verifier}: fail test if object state ends up incorrect</li> + * </ul> + */ +@Deprecated +public interface MethodRule { + /** + * Modifies the method-running {@link Statement} to implement an additional + * test-running rule. + * + * @param base The {@link Statement} to be modified + * @param method The method to be run + * @param target The object on with the method will be run. + * @return a new statement, which may be the same as {@code base}, + * a wrapper around {@code base}, or a completely new Statement. + */ + Statement apply(Statement base, FrameworkMethod method, Object target); +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/rules/RuleChain.java b/junit4/src/main/java/org/junit/rules/RuleChain.java new file mode 100644 index 0000000..8af3c05 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/RuleChain.java @@ -0,0 +1,99 @@ +/** + * + */ +package org.junit.rules; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * The RuleChain rule allows ordering of TestRules. You create a + * {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of + * {@link #around(TestRule)}: + * + * <pre> + * public static class UseRuleChain { + * @Rule + * public TestRule chain= RuleChain + * .outerRule(new LoggingRule("outer rule") + * .around(new LoggingRule("middle rule") + * .around(new LoggingRule("inner rule"); + * + * @Test + * public void example() { + * assertTrue(true); + * } + * } + * </pre> + * + * writes the log + * + * <pre> + * starting outer rule + * starting middle rule + * starting inner rule + * finished inner rule + * finished middle rule + * finished outer rule + * </pre> + */ +public class RuleChain implements TestRule { + private static final RuleChain EMPTY_CHAIN= new RuleChain( + Collections.<TestRule> emptyList()); + + private List<TestRule> rulesStartingWithInnerMost; + + /** + * Returns a {@code RuleChain} without a {@link TestRule}. This method may + * be the starting point of a {@code RuleChain}. + * + * @return a {@code RuleChain} without a {@link TestRule}. + */ + public static RuleChain emptyRuleChain() { + return EMPTY_CHAIN; + } + + /** + * Returns a {@code RuleChain} with a single {@link TestRule}. This method + * is the usual starting point of a {@code RuleChain}. + * + * @param outerRule + * the outer rule of the {@code RuleChain}. + * @return a {@code RuleChain} with a single {@link TestRule}. + */ + public static RuleChain outerRule(TestRule outerRule) { + return emptyRuleChain().around(outerRule); + } + + private RuleChain(List<TestRule> rules) { + this.rulesStartingWithInnerMost= rules; + } + + /** + * Create a new {@code RuleChain}, which encloses the {@code nextRule} with + * the rules of the current {@code RuleChain}. + * + * @param enclosedRule + * the rule to enclose. + * @return a new {@code RuleChain}. + */ + public RuleChain around(TestRule enclosedRule) { + List<TestRule> rulesOfNewChain= new ArrayList<TestRule>(); + rulesOfNewChain.add(enclosedRule); + rulesOfNewChain.addAll(rulesStartingWithInnerMost); + return new RuleChain(rulesOfNewChain); + } + + /** + * {@inheritDoc} + */ + public Statement apply(Statement base, Description description) { + for (TestRule each : rulesStartingWithInnerMost) + base= each.apply(base, description); + return base; + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/rules/RunRules.java b/junit4/src/main/java/org/junit/rules/RunRules.java new file mode 100644 index 0000000..d5905b9 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/RunRules.java @@ -0,0 +1,27 @@ +package org.junit.rules; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * Runs a collection of rules on a statement. + */ +public class RunRules extends Statement { + private final Statement statement; + + public RunRules(Statement base, Iterable<TestRule> rules, Description description) { + statement= applyAll(base, rules, description); + } + + @Override + public void evaluate() throws Throwable { + statement.evaluate(); + } + + private static Statement applyAll(Statement result, Iterable<TestRule> rules, + Description description) { + for (TestRule each : rules) + result= each.apply(result, description); + return result; + } +} diff --git a/junit4/src/main/java/org/junit/rules/TemporaryFolder.java b/junit4/src/main/java/org/junit/rules/TemporaryFolder.java new file mode 100644 index 0000000..a7c82aa --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/TemporaryFolder.java @@ -0,0 +1,113 @@ +package org.junit.rules; + +import java.io.File; +import java.io.IOException; + +import org.junit.Rule; + +/** + * The TemporaryFolder Rule allows creation of files and folders that are + * guaranteed to be deleted when the test method finishes (whether it passes or + * fails): + * + * <pre> + * public static class HasTempFolder { + * @Rule + * public TemporaryFolder folder= new TemporaryFolder(); + * + * @Test + * public void testUsingTempFolder() throws IOException { + * File createdFile= folder.newFile("myfile.txt"); + * File createdFolder= folder.newFolder("subfolder"); + * // ... + * } + * } + * </pre> + */ +public class TemporaryFolder extends ExternalResource { + private File folder; + + @Override + protected void before() throws Throwable { + create(); + } + + @Override + protected void after() { + delete(); + } + + // testing purposes only + /** + * for testing purposes only. Do not use. + */ + public void create() throws IOException { + folder= newFolder(); + } + + /** + * Returns a new fresh file with the given name under the temporary folder. + */ + public File newFile(String fileName) throws IOException { + File file= new File(getRoot(), fileName); + file.createNewFile(); + return file; + } + + /** + * Returns a new fresh file with a random name under the temporary folder. + */ + public File newFile() throws IOException { + return File.createTempFile("junit", null, folder); + } + + /** + * Returns a new fresh folder with the given name under the temporary folder. + */ + public File newFolder(String... folderNames) { + File file = getRoot(); + for (String folderName : folderNames) { + file = new File(file, folderName); + file.mkdir(); + } + return file; + } + + /** + * Returns a new fresh folder with a random name under the temporary + * folder. + */ + public File newFolder() throws IOException { + File createdFolder= File.createTempFile("junit", "", folder); + createdFolder.delete(); + createdFolder.mkdir(); + return createdFolder; + } + + /** + * @return the location of this temporary folder. + */ + public File getRoot() { + if (folder == null) { + throw new IllegalStateException("the temporary folder has not yet been created"); + } + return folder; + } + + /** + * Delete all files and folders under the temporary folder. + * Usually not called directly, since it is automatically applied + * by the {@link Rule} + */ + public void delete() { + recursiveDelete(folder); + } + + private void recursiveDelete(File file) { + File[] files= file.listFiles(); + if (files != null) + for (File each : files) + recursiveDelete(each); + file.delete(); + } +} diff --git a/junit4/src/main/java/org/junit/rules/TestName.java b/junit4/src/main/java/org/junit/rules/TestName.java new file mode 100644 index 0000000..c4ab9ce --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/TestName.java @@ -0,0 +1,39 @@ +package org.junit.rules; + +import org.junit.runner.Description; + +/** + * The TestName Rule makes the current test name available inside test methods: + * + * <pre> + * public class TestNameTest { + * @Rule + * public TestName name= new TestName(); + * + * @Test + * public void testA() { + * assertEquals("testA", name.getMethodName()); + * } + * + * @Test + * public void testB() { + * assertEquals("testB", name.getMethodName()); + * } + * } + * </pre> + */ +public class TestName extends TestWatcher { + private String fName; + + @Override + protected void starting(Description d) { + fName= d.getMethodName(); + } + + /** + * @return the name of the currently-running test method + */ + public String getMethodName() { + return fName; + } +} diff --git a/junit4/src/main/java/org/junit/rules/TestRule.java b/junit4/src/main/java/org/junit/rules/TestRule.java new file mode 100644 index 0000000..b7760c4 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/TestRule.java @@ -0,0 +1,54 @@ +package org.junit.rules; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * A TestRule is an alteration in how a test method, or set of test methods, + * is run and reported. A {@link TestRule} may add additional checks that cause + * a test that would otherwise fail to pass, or it may perform necessary setup or + * cleanup for tests, or it may observe test execution to report it elsewhere. + * {@link TestRule}s can do everything that could be done previously with + * methods annotated with {@link org.junit.Before}, + * {@link org.junit.After}, {@link org.junit.BeforeClass}, or + * {@link org.junit.AfterClass}, but they are more powerful, and more easily + * shared + * between projects and classes. + * + * The default JUnit test runners for suites and + * individual test cases recognize {@link TestRule}s introduced in two different + * ways. {@link org.junit.Rule} annotates method-level + * {@link TestRule}s, and {@link org.junit.ClassRule} + * annotates class-level {@link TestRule}s. See Javadoc for those annotations + * for more information. + * + * Multiple {@link TestRule}s can be applied to a test or suite execution. The + * {@link Statement} that executes the method or suite is passed to each annotated + * {@link org.junit.Rule} in turn, and each may return a substitute or modified + * {@link Statement}, which is passed to the next {@link org.junit.Rule}, if any. For + * examples of how this can be useful, see these provided TestRules, + * or write your own: + * + * <ul> + * <li>{@link ErrorCollector}: collect multiple errors in one test method</li> + * <li>{@link ExpectedException}: make flexible assertions about thrown exceptions</li> + * <li>{@link ExternalResource}: start and stop a server, for example</li> + * <li>{@link TemporaryFolder}: create fresh files, and delete after test</li> + * <li>{@link TestName}: remember the test name for use during the method</li> + * <li>{@link TestWatcher}: add logic at events during method execution</li> + * <li>{@link Timeout}: cause test to fail after a set time</li> + * <li>{@link Verifier}: fail test if object state ends up incorrect</li> + * </ul> + */ +public interface TestRule { + /** + * Modifies the method-running {@link Statement} to implement this + * test-running rule. + * + * @param base The {@link Statement} to be modified + * @param description A {@link Description} of the test implemented in {@code base} + * @return a new statement, which may be the same as {@code base}, + * a wrapper around {@code base}, or a completely new Statement. + */ + Statement apply(Statement base, Description description); +} diff --git a/junit4/src/main/java/org/junit/rules/TestWatcher.java b/junit4/src/main/java/org/junit/rules/TestWatcher.java new file mode 100644 index 0000000..351b449 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/TestWatcher.java @@ -0,0 +1,94 @@ +package org.junit.rules; + +import org.junit.internal.AssumptionViolatedException; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * TestWatcher is a base class for Rules that take note of the testing + * action, without modifying it. For example, this class will keep a log of each + * passing and failing test: + * + * <pre> + * public static class WatchmanTest { + * private static String watchedLog; + * + * @Rule + * public MethodRule watchman= new TestWatcher() { + * @Override + * protected void failed(Description d) { + * watchedLog+= d + "\n"; + * } + * + * @Override + * protected void succeeded(Description d) { + * watchedLog+= d + " " + "success!\n"; + * } + * }; + * + * @Test + * public void fails() { + * fail(); + * } + * + * @Test + * public void succeeds() { + * } + * } + * </pre> + */ +public abstract class TestWatcher implements TestRule { + public Statement apply(final Statement base, final Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + starting(description); + try { + base.evaluate(); + succeeded(description); + } catch (AssumptionViolatedException e) { + throw e; + } catch (Throwable t) { + failed(t, description); + throw t; + } finally { + finished(description); + } + } + }; + } + + /** + * Invoked when a test succeeds + * + * @param description + */ + protected void succeeded(Description description) { + } + + /** + * Invoked when a test fails + * + * @param e + * @param description + */ + protected void failed(Throwable e, Description description) { + } + + /** + * Invoked when a test is about to start + * + * @param description + */ + protected void starting(Description description) { + } + + + /** + * Invoked when a test method finishes (whether passing or failing) + * + * @param description + */ + protected void finished(Description description) { + } +} diff --git a/junit4/src/main/java/org/junit/rules/TestWatchman.java b/junit4/src/main/java/org/junit/rules/TestWatchman.java new file mode 100644 index 0000000..15daa64 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/TestWatchman.java @@ -0,0 +1,100 @@ +package org.junit.rules; + +import org.junit.internal.AssumptionViolatedException; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +/** + * TestWatchman is a base class for Rules that take note of the testing + * action, without modifying it. For example, this class will keep a log of each + * passing and failing test: + * + * <pre> + * public static class WatchmanTest { + * private static String watchedLog; + * + * @Rule + * public MethodRule watchman= new TestWatchman() { + * @Override + * public void failed(Throwable e, FrameworkMethod method) { + * watchedLog+= method.getName() + " " + e.getClass().getSimpleName() + * + "\n"; + * } + * + * @Override + * public void succeeded(FrameworkMethod method) { + * watchedLog+= method.getName() + " " + "success!\n"; + * } + * }; + * + * @Test + * public void fails() { + * fail(); + * } + * + * @Test + * public void succeeds() { + * } + * } + * </pre> + * + * @deprecated {@link MethodRule} is deprecated. + * Use {@link TestWatcher} implements {@link TestRule} instead. + */ +@Deprecated +public class TestWatchman implements MethodRule { + public Statement apply(final Statement base, final FrameworkMethod method, + Object target) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + starting(method); + try { + base.evaluate(); + succeeded(method); + } catch (AssumptionViolatedException e) { + throw e; + } catch (Throwable t) { + failed(t, method); + throw t; + } finally { + finished(method); + } + } + }; + } + + /** + * Invoked when a test method succeeds + * + * @param method + */ + public void succeeded(FrameworkMethod method) { + } + + /** + * Invoked when a test method fails + * + * @param e + * @param method + */ + public void failed(Throwable e, FrameworkMethod method) { + } + + /** + * Invoked when a test method is about to start + * + * @param method + */ + public void starting(FrameworkMethod method) { + } + + + /** + * Invoked when a test method finishes (whether passing or failing) + * + * @param method + */ + public void finished(FrameworkMethod method) { + } +} diff --git a/junit4/src/main/java/org/junit/rules/Timeout.java b/junit4/src/main/java/org/junit/rules/Timeout.java new file mode 100644 index 0000000..85ce6d6 --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/Timeout.java @@ -0,0 +1,49 @@ +/** + * + */ +package org.junit.rules; + +import org.junit.internal.runners.statements.FailOnTimeout; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * The Timeout Rule applies the same timeout to all test methods in a class: + * + * <pre> + * public static class HasGlobalTimeout { + * public static String log; + * + * @Rule + * public MethodRule globalTimeout= new Timeout(20); + * + * @Test + * public void testInfiniteLoop1() { + * log+= "ran1"; + * for (;;) { + * } + * } + * + * @Test + * public void testInfiniteLoop2() { + * log+= "ran2"; + * for (;;) { + * } + * } + * } + * </pre> + */ +public class Timeout implements TestRule { + private final int fMillis; + + /** + * @param millis the millisecond timeout + */ + public Timeout(int millis) { + fMillis= millis; + } + + public Statement apply(Statement base, Description description) { + return new FailOnTimeout(base, fMillis); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/rules/Verifier.java b/junit4/src/main/java/org/junit/rules/Verifier.java new file mode 100644 index 0000000..be1a55e --- /dev/null +++ b/junit4/src/main/java/org/junit/rules/Verifier.java @@ -0,0 +1,45 @@ +package org.junit.rules; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * Verifier is a base class for Rules like ErrorCollector, which can turn + * otherwise passing test methods into failing tests if a verification check is + * failed + * + * <pre> + * public static class ErrorLogVerifier() { + * private ErrorLog errorLog = new ErrorLog(); + * + * @Rule + * public MethodRule verifier = new Verifier() { + * @Override public void verify() { + * assertTrue(errorLog.isEmpty()); + * } + * } + * + * @Test public void testThatMightWriteErrorLog() { + * // ... + * } + * } + * </pre> + */ +public class Verifier implements TestRule { + public Statement apply(final Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + base.evaluate(); + verify(); + } + }; + } + + /** + * Override this to add verification logic. Overrides should throw an + * exception to indicate that verification failed. + */ + protected void verify() throws Throwable { + } +} diff --git a/junit4/src/main/java/org/junit/runner/Computer.java b/junit4/src/main/java/org/junit/runner/Computer.java new file mode 100644 index 0000000..939f702 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/Computer.java @@ -0,0 +1,40 @@ +package org.junit.runner; + +import org.junit.runners.Suite; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +/** + * Represents a strategy for computing runners and suites. + * WARNING: this class is very likely to undergo serious changes in version 4.8 and + * beyond. + */ +public class Computer { + /** + * Returns a new default computer, which runs tests in serial order + */ + public static Computer serial() { + return new Computer(); + } + + /** + * Create a suite for {@code classes}, building Runners with {@code builder}. + * Throws an InitializationError if Runner construction fails + */ + public Runner getSuite(final RunnerBuilder builder, + Class<?>[] classes) throws InitializationError { + return new Suite(new RunnerBuilder() { + @Override + public Runner runnerForClass(Class<?> testClass) throws Throwable { + return getRunner(builder, testClass); + } + }, classes); + } + + /** + * Create a single-class runner for {@code testClass}, using {@code builder} + */ + protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable { + return builder.runnerForClass(testClass); + } +} diff --git a/junit4/src/main/java/org/junit/runner/Describable.java b/junit4/src/main/java/org/junit/runner/Describable.java new file mode 100644 index 0000000..e59cc01 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/Describable.java @@ -0,0 +1,12 @@ +package org.junit.runner; + + +/** + * Represents an object that can describe itself + */ +public interface Describable { + /** + * @return a {@link Description} showing the tests to be run by the receiver + */ + public abstract Description getDescription(); +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runner/Description.java b/junit4/src/main/java/org/junit/runner/Description.java new file mode 100644 index 0000000..b3083d5 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/Description.java @@ -0,0 +1,242 @@ +package org.junit.runner; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * <p>A <code>Description</code> describes a test which is to be run or has been run. <code>Descriptions</code> + * can be atomic (a single test) or compound (containing children tests). <code>Descriptions</code> are used + * to provide feedback about the tests that are about to run (for example, the tree view + * visible in many IDEs) or tests that have been run (for example, the failures view).</p> + * + * <p><code>Descriptions</code> are implemented as a single class rather than a Composite because + * they are entirely informational. They contain no logic aside from counting their tests.</p> + * + * <p>In the past, we used the raw {@link junit.framework.TestCase}s and {@link junit.framework.TestSuite}s + * to display the tree of tests. This was no longer viable in JUnit 4 because atomic tests no longer have + * a superclass below {@link Object}. We needed a way to pass a class and name together. Description + * emerged from this.</p> + * + * @see org.junit.runner.Request + * @see org.junit.runner.Runner + */ +public class Description implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * Create a <code>Description</code> named <code>name</code>. + * Generally, you will add children to this <code>Description</code>. + * @param name the name of the <code>Description</code> + * @param annotations + * @return a <code>Description</code> named <code>name</code> + */ + public static Description createSuiteDescription(String name, Annotation... annotations) { + if (name.length() == 0) + throw new IllegalArgumentException("name must have non-zero length"); + return new Description(name, annotations); + } + + /** + * Create a <code>Description</code> of a single test named <code>name</code> in the class <code>clazz</code>. + * Generally, this will be a leaf <code>Description</code>. + * @param clazz the class of the test + * @param name the name of the test (a method name for test annotated with {@link org.junit.Test}) + * @param annotations meta-data about the test, for downstream interpreters + * @return a <code>Description</code> named <code>name</code> + */ + public static Description createTestDescription(Class<?> clazz, String name, Annotation... annotations) { + return new Description(String.format("%s(%s)", name, clazz.getName()), annotations); + } + + /** + * Create a <code>Description</code> of a single test named <code>name</code> in the class <code>clazz</code>. + * Generally, this will be a leaf <code>Description</code>. + * (This remains for binary compatibility with clients of JUnit 4.3) + * @param clazz the class of the test + * @param name the name of the test (a method name for test annotated with {@link org.junit.Test}) + * @return a <code>Description</code> named <code>name</code> + */ + public static Description createTestDescription(Class<?> clazz, String name) { + return createTestDescription(clazz, name, new Annotation[0]); + } + + /** + * Create a <code>Description</code> named after <code>testClass</code> + * @param testClass A {@link Class} containing tests + * @return a <code>Description</code> of <code>testClass</code> + */ + public static Description createSuiteDescription(Class<?> testClass) { + return new Description(testClass.getName(), testClass.getAnnotations()); + } + + /** + * Describes a Runner which runs no tests + */ + public static final Description EMPTY= new Description("No Tests"); + + /** + * Describes a step in the test-running mechanism that goes so wrong no + * other description can be used (for example, an exception thrown from a Runner's + * constructor + */ + public static final Description TEST_MECHANISM= new Description("Test mechanism"); + + private final ArrayList<Description> fChildren= new ArrayList<Description>(); + private final String fDisplayName; + + private final Annotation[] fAnnotations; + + private Description(final String displayName, Annotation... annotations) { + fDisplayName= displayName; + fAnnotations= annotations; + } + + /** + * @return a user-understandable label + */ + public String getDisplayName() { + return fDisplayName; + } + + /** + * Add <code>Description</code> as a child of the receiver. + * @param description the soon-to-be child. + */ + public void addChild(Description description) { + getChildren().add(description); + } + + /** + * @return the receiver's children, if any + */ + public ArrayList<Description> getChildren() { + return fChildren; + } + + /** + * @return <code>true</code> if the receiver is a suite + */ + public boolean isSuite() { + return !isTest(); + } + + /** + * @return <code>true</code> if the receiver is an atomic test + */ + public boolean isTest() { + return getChildren().isEmpty(); + } + + /** + * @return the total number of atomic tests in the receiver + */ + public int testCount() { + if (isTest()) + return 1; + int result= 0; + for (Description child : getChildren()) + result+= child.testCount(); + return result; + } + + @Override + public int hashCode() { + return getDisplayName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Description)) + return false; + Description d = (Description) obj; + return getDisplayName().equals(d.getDisplayName()); + } + + @Override + public String toString() { + return getDisplayName(); + } + + /** + * @return true if this is a description of a Runner that runs no tests + */ + public boolean isEmpty() { + return equals(EMPTY); + } + + /** + * @return a copy of this description, with no children (on the assumption that some of the + * children will be added back) + */ + public Description childlessCopy() { + return new Description(fDisplayName, fAnnotations); + } + + /** + * @return the annotation of type annotationType that is attached to this description node, + * or null if none exists + */ + public <T extends Annotation> T getAnnotation(Class<T> annotationType) { + for (Annotation each : fAnnotations) + if (each.annotationType().equals(annotationType)) + return annotationType.cast(each); + return null; + } + + /** + * @return all of the annotations attached to this description node + */ + public Collection<Annotation> getAnnotations() { + return Arrays.asList(fAnnotations); + } + + /** + * @return If this describes a method invocation, + * the class of the test instance. + */ + public Class<?> getTestClass() { + String name= getClassName(); + if (name == null) + return null; + try { + return Class.forName(name); + } catch (ClassNotFoundException e) { + return null; + } + } + + /** + * @return If this describes a method invocation, + * the name of the class of the test instance + */ + public String getClassName() { + Matcher matcher= methodStringMatcher(); + return matcher.matches() + ? matcher.group(2) + : toString(); + } + + /** + * @return If this describes a method invocation, + * the name of the method (or null if not) + */ + public String getMethodName() { + return parseMethod(); + } + + private String parseMethod() { + Matcher matcher= methodStringMatcher(); + if (matcher.matches()) + return matcher.group(1); + return null; + } + + private Matcher methodStringMatcher() { + return Pattern.compile("(.*)\\((.*)\\)").matcher(toString()); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runner/JUnitCore.java b/junit4/src/main/java/org/junit/runner/JUnitCore.java new file mode 100644 index 0000000..2fcd3b3 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/JUnitCore.java @@ -0,0 +1,186 @@ +package org.junit.runner; + +import java.util.ArrayList; +import java.util.List; + +import junit.runner.Version; +import org.junit.internal.JUnitSystem; +import org.junit.internal.RealSystem; +import org.junit.internal.TextListener; +import org.junit.internal.runners.JUnit38ClassRunner; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; + +/** + * <code>JUnitCore</code> is a facade for running tests. It supports running JUnit 4 tests, + * JUnit 3.8.x tests, and mixtures. To run tests from the command line, run + * <code>java org.junit.runner.JUnitCore TestClass1 TestClass2 ...</code>. + * For one-shot test runs, use the static method {@link #runClasses(Class[])}. + * If you want to add special listeners, + * create an instance of {@link org.junit.runner.JUnitCore} first and use it to run the tests. + * + * @see org.junit.runner.Result + * @see org.junit.runner.notification.RunListener + * @see org.junit.runner.Request + */ +public class JUnitCore { + private RunNotifier fNotifier; + + /** + * Create a new <code>JUnitCore</code> to run tests. + */ + public JUnitCore() { + fNotifier= new RunNotifier(); + } + + /** + * Run the tests contained in the classes named in the <code>args</code>. + * If all tests run successfully, exit with a status of 0. Otherwise exit with a status of 1. + * Write feedback while tests are running and write + * stack traces for all failed tests after the tests all complete. + * @param args names of classes in which to find tests to run + */ + public static void main(String... args) { + runMainAndExit(new RealSystem(), args); + } + + /** + * Do not use. Testing purposes only. + * @param system + */ + public static void runMainAndExit(JUnitSystem system, String... args) { + Result result= new JUnitCore().runMain(system, args); + system.exit(result.wasSuccessful() ? 0 : 1); + } + + /** + * Run the tests contained in <code>classes</code>. Write feedback while the tests + * are running and write stack traces for all failed tests after all tests complete. This is + * similar to {@link #main(String[])}, but intended to be used programmatically. + * @param computer Helps construct Runners from classes + * @param classes Classes in which to find tests + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public static Result runClasses(Computer computer, Class<?>... classes) { + return new JUnitCore().run(computer, classes); + } + /** + * Run the tests contained in <code>classes</code>. Write feedback while the tests + * are running and write stack traces for all failed tests after all tests complete. This is + * similar to {@link #main(String[])}, but intended to be used programmatically. + * @param classes Classes in which to find tests + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public static Result runClasses(Class<?>... classes) { + return new JUnitCore().run(defaultComputer(), classes); + } + + /** + * Do not use. Testing purposes only. + * @param system + */ + public Result runMain(JUnitSystem system, String... args) { + system.out().println("JUnit version " + Version.id()); + List<Class<?>> classes= new ArrayList<Class<?>>(); + List<Failure> missingClasses= new ArrayList<Failure>(); + for (String each : args) + try { + classes.add(Class.forName(each)); + } catch (ClassNotFoundException e) { + system.out().println("Could not find class: " + each); + Description description= Description.createSuiteDescription(each); + Failure failure= new Failure(description, e); + missingClasses.add(failure); + } + RunListener listener= new TextListener(system); + addListener(listener); + Result result= run(classes.toArray(new Class[0])); + for (Failure each : missingClasses) + result.getFailures().add(each); + return result; + } + + /** + * @return the version number of this release + */ + public String getVersion() { + return Version.id(); + } + + /** + * Run all the tests in <code>classes</code>. + * @param classes the classes containing tests + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public Result run(Class<?>... classes) { + return run(Request.classes(defaultComputer(), classes)); + } + + /** + * Run all the tests in <code>classes</code>. + * @param computer Helps construct Runners from classes + * @param classes the classes containing tests + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public Result run(Computer computer, Class<?>... classes) { + return run(Request.classes(computer, classes)); + } + + /** + * Run all the tests contained in <code>request</code>. + * @param request the request describing tests + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public Result run(Request request) { + return run(request.getRunner()); + } + + /** + * Run all the tests contained in JUnit 3.8.x <code>test</code>. Here for backward compatibility. + * @param test the old-style test + * @return a {@link Result} describing the details of the test run and the failed tests. + */ + public Result run(junit.framework.Test test) { + return run(new JUnit38ClassRunner(test)); + } + + /** + * Do not use. Testing purposes only. + */ + public Result run(Runner runner) { + Result result= new Result(); + RunListener listener= result.createListener(); + fNotifier.addFirstListener(listener); + try { + fNotifier.fireTestRunStarted(runner.getDescription()); + runner.run(fNotifier); + fNotifier.fireTestRunFinished(result); + } finally { + removeListener(listener); + } + return result; + } + + /** + * Add a listener to be notified as the tests run. + * @param listener the listener to add + * @see org.junit.runner.notification.RunListener + */ + public void addListener(RunListener listener) { + fNotifier.addListener(listener); + } + + /** + * Remove a listener. + * @param listener the listener to remove + */ + public void removeListener(RunListener listener) { + fNotifier.removeListener(listener); + } + + static Computer defaultComputer() { + return new Computer(); + } + +} diff --git a/junit4/src/main/java/org/junit/runner/Request.java b/junit4/src/main/java/org/junit/runner/Request.java new file mode 100644 index 0000000..310b915 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/Request.java @@ -0,0 +1,161 @@ +package org.junit.runner; + +import java.util.Comparator; + +import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; +import org.junit.internal.requests.ClassRequest; +import org.junit.internal.requests.FilterRequest; +import org.junit.internal.requests.SortingRequest; +import org.junit.internal.runners.ErrorReportingRunner; +import org.junit.runner.manipulation.Filter; +import org.junit.runners.model.InitializationError; + +/** + * <p>A <code>Request</code> is an abstract description of tests to be run. Older versions of + * JUnit did not need such a concept--tests to be run were described either by classes containing + * tests or a tree of {@link org.junit.Test}s. However, we want to support filtering and sorting, + * so we need a more abstract specification than the tests themselves and a richer + * specification than just the classes.</p> + * + * <p>The flow when JUnit runs tests is that a <code>Request</code> specifies some tests to be run -> + * a {@link org.junit.runner.Runner} is created for each class implied by the <code>Request</code> -> + * the {@link org.junit.runner.Runner} returns a detailed {@link org.junit.runner.Description} + * which is a tree structure of the tests to be run.</p> + */ +public abstract class Request { + /** + * Create a <code>Request</code> that, when processed, will run a single test. + * This is done by filtering out all other tests. This method is used to support rerunning + * single tests. + * @param clazz the class of the test + * @param methodName the name of the test + * @return a <code>Request</code> that will cause a single test be run + */ + public static Request method(Class<?> clazz, String methodName) { + Description method= Description.createTestDescription(clazz, methodName); + return Request.aClass(clazz).filterWith(method); + } + + /** + * Create a <code>Request</code> that, when processed, will run all the tests + * in a class. The odd name is necessary because <code>class</code> is a reserved word. + * @param clazz the class containing the tests + * @return a <code>Request</code> that will cause all tests in the class to be run + */ + public static Request aClass(Class<?> clazz) { + return new ClassRequest(clazz); + } + + /** + * Create a <code>Request</code> that, when processed, will run all the tests + * in a class. If the class has a suite() method, it will be ignored. + * @param clazz the class containing the tests + * @return a <code>Request</code> that will cause all tests in the class to be run + */ + public static Request classWithoutSuiteMethod(Class<?> clazz) { + return new ClassRequest(clazz, false); + } + + /** + * Create a <code>Request</code> that, when processed, will run all the tests + * in a set of classes. + * @param computer Helps construct Runners from classes + * @param classes the classes containing the tests + * @return a <code>Request</code> that will cause all tests in the classes to be run + */ + public static Request classes(Computer computer, Class<?>... classes) { + try { + AllDefaultPossibilitiesBuilder builder= new AllDefaultPossibilitiesBuilder(true); + Runner suite= computer.getSuite(builder, classes); + return runner(suite); + } catch (InitializationError e) { + throw new RuntimeException( + "Bug in saff's brain: Suite constructor, called as above, should always complete"); + } + } + + /** + * Create a <code>Request</code> that, when processed, will run all the tests + * in a set of classes with the default <code>Computer</code>. + * @param classes the classes containing the tests + * @return a <code>Request</code> that will cause all tests in the classes to be run + */ + public static Request classes(Class<?>... classes) { + return classes(JUnitCore.defaultComputer(), classes); + } + + + /** + * Not used within JUnit. Clients should simply instantiate ErrorReportingRunner themselves + */ + @Deprecated + public static Request errorReport(Class<?> klass, Throwable cause) { + return runner(new ErrorReportingRunner(klass, cause)); + } + + /** + * @param runner the runner to return + * @return a <code>Request</code> that will run the given runner when invoked + */ + public static Request runner(final Runner runner) { + return new Request(){ + @Override + public Runner getRunner() { + return runner; + } + }; + } + + /** + * Returns a {@link Runner} for this Request + * @return corresponding {@link Runner} for this Request + */ + public abstract Runner getRunner(); + + /** + * Returns a Request that only contains those tests that should run when + * <code>filter</code> is applied + * @param filter The {@link Filter} to apply to this Request + * @return the filtered Request + */ + public Request filterWith(Filter filter) { + return new FilterRequest(this, filter); + } + + /** + * Returns a Request that only runs contains tests whose {@link Description} + * equals <code>desiredDescription</code> + * @param desiredDescription {@link Description} of those tests that should be run + * @return the filtered Request + */ + public Request filterWith(final Description desiredDescription) { + return filterWith(Filter.matchMethodDescription(desiredDescription)); + } + + /** + * Returns a Request whose Tests can be run in a certain order, defined by + * <code>comparator</code> + * + * For example, here is code to run a test suite in alphabetical order: + * + * <pre> + private static Comparator<Description> forward() { + return new Comparator<Description>() { + public int compare(Description o1, Description o2) { + return o1.getDisplayName().compareTo(o2.getDisplayName()); + } + }; + } + + public static main() { + new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward())); + } + * </pre> + * + * @param comparator definition of the order of the tests in this Request + * @return a Request with ordered Tests + */ + public Request sortWith(Comparator<Description> comparator) { + return new SortingRequest(this, comparator); + } +} diff --git a/junit4/src/main/java/org/junit/runner/Result.java b/junit4/src/main/java/org/junit/runner/Result.java new file mode 100644 index 0000000..edfb97c --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/Result.java @@ -0,0 +1,106 @@ +package org.junit.runner; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +/** + * A <code>Result</code> collects and summarizes information from running multiple + * tests. Since tests are expected to run correctly, successful tests are only noted in + * the count of tests that ran. + */ +public class Result implements Serializable { + private static final long serialVersionUID = 1L; + private AtomicInteger fCount = new AtomicInteger(); + private AtomicInteger fIgnoreCount= new AtomicInteger(); + private final List<Failure> fFailures= Collections.synchronizedList(new ArrayList<Failure>()); + private long fRunTime= 0; + private long fStartTime; + + /** + * @return the number of tests run + */ + public int getRunCount() { + return fCount.get(); + } + + /** + * @return the number of tests that failed during the run + */ + public int getFailureCount() { + return fFailures.size(); + } + + /** + * @return the number of milliseconds it took to run the entire suite to run + */ + public long getRunTime() { + return fRunTime; + } + + /** + * @return the {@link Failure}s describing tests that failed and the problems they encountered + */ + public List<Failure> getFailures() { + return fFailures; + } + + /** + * @return the number of tests ignored during the run + */ + public int getIgnoreCount() { + return fIgnoreCount.get(); + } + + /** + * @return <code>true</code> if all tests succeeded + */ + public boolean wasSuccessful() { + return getFailureCount() == 0; + } + + private class Listener extends RunListener { + @Override + public void testRunStarted(Description description) throws Exception { + fStartTime= System.currentTimeMillis(); + } + + @Override + public void testRunFinished(Result result) throws Exception { + long endTime= System.currentTimeMillis(); + fRunTime+= endTime - fStartTime; + } + + @Override + public void testFinished(Description description) throws Exception { + fCount.getAndIncrement(); + } + + @Override + public void testFailure(Failure failure) throws Exception { + fFailures.add(failure); + } + + @Override + public void testIgnored(Description description) throws Exception { + fIgnoreCount.getAndIncrement(); + } + + @Override + public void testAssumptionFailure(Failure failure) { + // do nothing: same as passing (for 4.5; may change in 4.6) + } + } + + /** + * Internal use only. + */ + public RunListener createListener() { + return new Listener(); + } +} diff --git a/junit4/src/main/java/org/junit/runner/RunWith.java b/junit4/src/main/java/org/junit/runner/RunWith.java new file mode 100644 index 0000000..602edf0 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/RunWith.java @@ -0,0 +1,34 @@ +package org.junit.runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * When a class is annotated with <code>@RunWith</code> or extends a class annotated + * with <code>@RunWith</code>, JUnit will invoke the class it references to run the + * tests in that class instead of the runner built into JUnit. We added this feature late + * in development. While it seems powerful we expect the runner API to change as we learn + * how people really use it. Some of the classes that are currently internal will likely + * be refined and become public. + * + * For example, suites in JUnit 4 are built using RunWith, and a custom runner named Suite: + * + * <pre> + * @RunWith(Suite.class) + * @SuiteClasses({ATest.class, BTest.class, CTest.class}) + * public class ABCSuite { + * } + * </pre> + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface RunWith { + /** + * @return a Runner class (must have a constructor that takes a single Class to run) + */ + Class<? extends Runner> value(); +} diff --git a/junit4/src/main/java/org/junit/runner/Runner.java b/junit4/src/main/java/org/junit/runner/Runner.java new file mode 100644 index 0000000..39e424f --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/Runner.java @@ -0,0 +1,40 @@ +package org.junit.runner; + +import org.junit.runner.notification.RunNotifier; + +/** + * A <code>Runner</code> runs tests and notifies a {@link org.junit.runner.notification.RunNotifier} + * of significant events as it does so. You will need to subclass <code>Runner</code> + * when using {@link org.junit.runner.RunWith} to invoke a custom runner. When creating + * a custom runner, in addition to implementing the abstract methods here you must + * also provide a constructor that takes as an argument the {@link Class} containing + * the tests. + * <p/> + * The default runner implementation guarantees that the instances of the test case + * class will be constructed immediately before running the test and that the runner + * will retain no reference to the test case instances, generally making them + * available for garbage collection. + * + * @see org.junit.runner.Description + * @see org.junit.runner.RunWith + */ +public abstract class Runner implements Describable { + /* (non-Javadoc) + * @see org.junit.runner.Describable#getDescription() + */ + public abstract Description getDescription(); + + /** + * Run the tests for this runner. + * @param notifier will be notified of events while tests are being run--tests being + * started, finishing, and failing + */ + public abstract void run(RunNotifier notifier); + + /** + * @return the number of tests to be run by the receiver + */ + public int testCount() { + return getDescription().testCount(); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runner/manipulation/Filter.java b/junit4/src/main/java/org/junit/runner/manipulation/Filter.java new file mode 100644 index 0000000..c0f31b0 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/manipulation/Filter.java @@ -0,0 +1,114 @@ +package org.junit.runner.manipulation; + +import org.junit.runner.Description; +import org.junit.runner.Request; + +/** + * The canonical case of filtering is when you want to run a single test method in a class. Rather + * than introduce runner API just for that one case, JUnit provides a general filtering mechanism. + * If you want to filter the tests to be run, extend <code>Filter</code> and apply an instance of + * your filter to the {@link org.junit.runner.Request} before running it (see + * {@link org.junit.runner.JUnitCore#run(Request)}. Alternatively, apply a <code>Filter</code> to + * a {@link org.junit.runner.Runner} before running tests (for example, in conjunction with + * {@link org.junit.runner.RunWith}. + */ +public abstract class Filter { + /** + * A null <code>Filter</code> that passes all tests through. + */ + public static Filter ALL= new Filter() { + @Override + public boolean shouldRun(Description description) { + return true; + } + + @Override + public String describe() { + return "all tests"; + } + + @Override + public void apply(Object child) throws NoTestsRemainException { + // do nothing + } + + @Override + public Filter intersect(Filter second) { + return second; + } + }; + + /** + * Returns a {@code Filter} that only runs the single method described by + * {@code desiredDescription} + */ + public static Filter matchMethodDescription(final Description desiredDescription) { + return new Filter() { + @Override + public boolean shouldRun(Description description) { + if (description.isTest()) + return desiredDescription.equals(description); + + // explicitly check if any children want to run + for (Description each : description.getChildren()) + if (shouldRun(each)) + return true; + return false; + } + + @Override + public String describe() { + return String.format("Method %s", desiredDescription.getDisplayName()); + } + }; + } + + + /** + * @param description the description of the test to be run + * @return <code>true</code> if the test should be run + */ + public abstract boolean shouldRun(Description description); + + /** + * Returns a textual description of this Filter + * @return a textual description of this Filter + */ + public abstract String describe(); + + /** + * Invoke with a {@link org.junit.runner.Runner} to cause all tests it intends to run + * to first be checked with the filter. Only those that pass the filter will be run. + * @param child the runner to be filtered by the receiver + * @throws NoTestsRemainException if the receiver removes all tests + */ + public void apply(Object child) throws NoTestsRemainException { + if (!(child instanceof Filterable)) + return; + Filterable filterable= (Filterable) child; + filterable.filter(this); + } + + /** + * Returns a new Filter that accepts the intersection of the tests accepted + * by this Filter and {@code second} + */ + public Filter intersect(final Filter second) { + if (second == this || second == ALL) { + return this; + } + final Filter first= this; + return new Filter() { + @Override + public boolean shouldRun(Description description) { + return first.shouldRun(description) + && second.shouldRun(description); + } + + @Override + public String describe() { + return first.describe() + " and " + second.describe(); + } + }; + } +} diff --git a/junit4/src/main/java/org/junit/runner/manipulation/Filterable.java b/junit4/src/main/java/org/junit/runner/manipulation/Filterable.java new file mode 100644 index 0000000..782c0f7 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/manipulation/Filterable.java @@ -0,0 +1,16 @@ +package org.junit.runner.manipulation; + +/** + * Runners that allow filtering should implement this interface. Implement {@link #filter(Filter)} + * to remove tests that don't pass the filter. + */ +public interface Filterable { + + /** + * Remove tests that don't pass the parameter <code>filter</code>. + * @param filter the {@link Filter} to apply + * @throws NoTestsRemainException if all tests are filtered out + */ + void filter(Filter filter) throws NoTestsRemainException; + +} diff --git a/junit4/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java b/junit4/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java new file mode 100644 index 0000000..03fb3bf --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java @@ -0,0 +1,8 @@ +package org.junit.runner.manipulation; + +/** + * Thrown when a filter removes all tests from a runner. + */ +public class NoTestsRemainException extends Exception { + private static final long serialVersionUID = 1L; +} diff --git a/junit4/src/main/java/org/junit/runner/manipulation/Sortable.java b/junit4/src/main/java/org/junit/runner/manipulation/Sortable.java new file mode 100644 index 0000000..fec1d0c --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/manipulation/Sortable.java @@ -0,0 +1,17 @@ +package org.junit.runner.manipulation; + +/** + * Interface for runners that allow sorting of tests. By sorting tests based on when they last failed, most recently + * failed first, you can reduce the average time to the first test failing. Test sorting should not be used to + * cope with order dependencies between tests. Tests that are isolated from each other are less + * expensive to maintain and can be run individually. + */ +public interface Sortable { + + /** + * Sorts the tests using <code>sorter</code> + * @param sorter the {@link Sorter} to use for sorting the tests + */ + public void sort(Sorter sorter); + +} diff --git a/junit4/src/main/java/org/junit/runner/manipulation/Sorter.java b/junit4/src/main/java/org/junit/runner/manipulation/Sorter.java new file mode 100644 index 0000000..242df14 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/manipulation/Sorter.java @@ -0,0 +1,46 @@ +package org.junit.runner.manipulation; + +import java.util.Comparator; + +import org.junit.runner.Description; + +/** + * A <code>Sorter</code> orders tests. In general you will not need + * to use a <code>Sorter</code> directly. Instead, use {@link org.junit.runner.Request#sortWith(Comparator)}. + * + * + */ +public class Sorter implements Comparator<Description> { + /** + * NULL is a <code>Sorter</code> that leaves elements in an undefined order + */ + public static Sorter NULL= new Sorter(new Comparator<Description>() { + public int compare(Description o1, Description o2) { + return 0; + }}); + private final Comparator<Description> fComparator; + + /** + * Creates a <code>Sorter</code> that uses <code>comparator</code> + * to sort tests + * @param comparator the {@link Comparator} to use when sorting tests + */ + public Sorter(Comparator<Description> comparator) { + fComparator= comparator; + } + + /** + * Sorts the test in <code>runner</code> using <code>comparator</code> + * @param object + */ + public void apply(Object object) { + if (object instanceof Sortable) { + Sortable sortable = (Sortable) object; + sortable.sort(this); + } + } + + public int compare(Description o1, Description o2) { + return fComparator.compare(o1, o2); + } +} diff --git a/junit4/src/main/java/org/junit/runner/manipulation/package-info.java b/junit4/src/main/java/org/junit/runner/manipulation/package-info.java new file mode 100644 index 0000000..ba5c3b2 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/manipulation/package-info.java @@ -0,0 +1,7 @@ +/** + * Provides classes to {@link org.junit.runner.manipulation.Filter filter} or {@link org.junit.runner.manipulation.Sorter sort} tests. + * + * @since 4.0 + * @see org.junit.runner.Runner + */ +package org.junit.runner.manipulation;
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runner/notification/Failure.java b/junit4/src/main/java/org/junit/runner/notification/Failure.java new file mode 100644 index 0000000..501caa5 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/notification/Failure.java @@ -0,0 +1,79 @@ +package org.junit.runner.notification; + +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.StringWriter; + +import org.junit.runner.Description; + +/** + * A <code>Failure</code> holds a description of the failed test and the + * exception that was thrown while running it. In most cases the {@link org.junit.runner.Description} + * will be of a single test. However, if problems are encountered while constructing the + * test (for example, if a {@link org.junit.BeforeClass} method is not static), it may describe + * something other than a single test. + */ +public class Failure implements Serializable { + private static final long serialVersionUID = 1L; + private final Description fDescription; + private final Throwable fThrownException; + + /** + * Constructs a <code>Failure</code> with the given description and exception. + * @param description a {@link org.junit.runner.Description} of the test that failed + * @param thrownException the exception that was thrown while running the test + */ + public Failure(Description description, Throwable thrownException) { + fThrownException = thrownException; + fDescription= description; + } + + /** + * @return a user-understandable label for the test + */ + public String getTestHeader() { + return fDescription.getDisplayName(); + } + + /** + * @return the raw description of the context of the failure. + */ + public Description getDescription() { + return fDescription; + } + + /** + * @return the exception thrown + */ + + public Throwable getException() { + return fThrownException; + } + + @Override + public String toString() { + StringBuffer buffer= new StringBuffer(); + buffer.append(getTestHeader() + ": "+fThrownException.getMessage()); + return buffer.toString(); + } + + /** + * Convenience method + * @return the printed form of the exception + */ + public String getTrace() { + StringWriter stringWriter= new StringWriter(); + PrintWriter writer= new PrintWriter(stringWriter); + getException().printStackTrace(writer); + StringBuffer buffer= stringWriter.getBuffer(); + return buffer.toString(); + } + + /** + * Convenience method + * @return the message of the thrown exception + */ + public String getMessage() { + return getException().getMessage(); + } +} diff --git a/junit4/src/main/java/org/junit/runner/notification/RunListener.java b/junit4/src/main/java/org/junit/runner/notification/RunListener.java new file mode 100644 index 0000000..ffe8134 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/notification/RunListener.java @@ -0,0 +1,93 @@ +package org.junit.runner.notification; + +import org.junit.internal.AssumptionViolatedException; +import org.junit.runner.Description; +import org.junit.runner.Result; + +/** + * <p>If you need to respond to the events during a test run, extend <code>RunListener</code> + * and override the appropriate methods. If a listener throws an exception while processing a + * test event, it will be removed for the remainder of the test run.</p> + * + * <p>For example, suppose you have a <code>Cowbell</code> + * class that you want to make a noise whenever a test fails. You could write: + * <pre> + * public class RingingListener extends RunListener { + * public void testFailure(Failure failure) { + * Cowbell.ring(); + * } + * } + * </pre> + * </p> + * + * <p>To invoke your listener, you need to run your tests through <code>JUnitCore</code>. + * <pre> + * public void main(String... args) { + * JUnitCore core= new JUnitCore(); + * core.addListener(new RingingListener()); + * core.run(MyTestClass.class); + * } + * </pre> + * </p> + * @see org.junit.runner.JUnitCore + */ +public class RunListener { + + /** + * Called before any tests have been run. + * @param description describes the tests to be run + */ + public void testRunStarted(Description description) throws Exception { + } + + /** + * Called when all tests have finished + * @param result the summary of the test run, including all the tests that failed + */ + public void testRunFinished(Result result) throws Exception { + } + + /** + * Called when an atomic test is about to be started. + * @param description the description of the test that is about to be run + * (generally a class and method name) + */ + public void testStarted(Description description) throws Exception { + } + + /** + * Called when an atomic test has finished, whether the test succeeds or fails. + * @param description the description of the test that just ran + */ + public void testFinished(Description description) throws Exception { + } + + /** + * Called when an atomic test fails. + * @param failure describes the test that failed and the exception that was thrown + */ + public void testFailure(Failure failure) throws Exception { + } + + /** + * Called when an atomic test flags that it assumes a condition that is + * false + * + * @param failure + * describes the test that failed and the + * {@link AssumptionViolatedException} that was thrown + */ + public void testAssumptionFailure(Failure failure) { + } + + /** + * Called when a test will not be run, generally because a test method is annotated + * with {@link org.junit.Ignore}. + * + * @param description describes the test that will not be run + */ + public void testIgnored(Description description) throws Exception { + } +} + + diff --git a/junit4/src/main/java/org/junit/runner/notification/RunNotifier.java b/junit4/src/main/java/org/junit/runner/notification/RunNotifier.java new file mode 100644 index 0000000..d0f6c85 --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/notification/RunNotifier.java @@ -0,0 +1,166 @@ +package org.junit.runner.notification; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.junit.internal.AssumptionViolatedException; +import org.junit.runner.Description; +import org.junit.runner.Result; + +/** + * If you write custom runners, you may need to notify JUnit of your progress running tests. + * Do this by invoking the <code>RunNotifier</code> passed to your implementation of + * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to + * move {@link #fireTestRunStarted(Description)} and {@link #fireTestRunFinished(Result)} + * to a separate class since they should only be called once per run. + */ +public class RunNotifier { + private final List<RunListener> fListeners= + Collections.synchronizedList(new ArrayList<RunListener>()); + private boolean fPleaseStop= false; + + /** Internal use only + */ + public void addListener(RunListener listener) { + fListeners.add(listener); + } + + /** Internal use only + */ + public void removeListener(RunListener listener) { + fListeners.remove(listener); + } + + private abstract class SafeNotifier { + void run() { + synchronized (fListeners) { + for (Iterator<RunListener> all= fListeners.iterator(); all.hasNext();) + try { + notifyListener(all.next()); + } catch (Exception e) { + all.remove(); // Remove the offending listener first to avoid an infinite loop + fireTestFailure(new Failure(Description.TEST_MECHANISM, e)); + } + } + } + + abstract protected void notifyListener(RunListener each) throws Exception; + } + + /** + * Do not invoke. + */ + public void fireTestRunStarted(final Description description) { + new SafeNotifier() { + @Override + protected void notifyListener(RunListener each) throws Exception { + each.testRunStarted(description); + }; + }.run(); + } + + /** + * Do not invoke. + */ + public void fireTestRunFinished(final Result result) { + new SafeNotifier() { + @Override + protected void notifyListener(RunListener each) throws Exception { + each.testRunFinished(result); + }; + }.run(); + } + + /** + * Invoke to tell listeners that an atomic test is about to start. + * @param description the description of the atomic test (generally a class and method name) + * @throws StoppedByUserException thrown if a user has requested that the test run stop + */ + public void fireTestStarted(final Description description) throws StoppedByUserException { + if (fPleaseStop) + throw new StoppedByUserException(); + new SafeNotifier() { + @Override + protected void notifyListener(RunListener each) throws Exception { + each.testStarted(description); + }; + }.run(); + } + + /** + * Invoke to tell listeners that an atomic test failed. + * @param failure the description of the test that failed and the exception thrown + */ + public void fireTestFailure(final Failure failure) { + new SafeNotifier() { + @Override + protected void notifyListener(RunListener each) throws Exception { + each.testFailure(failure); + }; + }.run(); + } + + /** + * Invoke to tell listeners that an atomic test flagged that it assumed + * something false. + * + * @param failure + * the description of the test that failed and the + * {@link AssumptionViolatedException} thrown + */ + public void fireTestAssumptionFailed(final Failure failure) { + new SafeNotifier() { + @Override + protected void notifyListener(RunListener each) throws Exception { + each.testAssumptionFailure(failure); + }; + }.run(); + } + + /** + * Invoke to tell listeners that an atomic test was ignored. + * @param description the description of the ignored test + */ + public void fireTestIgnored(final Description description) { + new SafeNotifier() { + @Override + protected void notifyListener(RunListener each) throws Exception { + each.testIgnored(description); + } + }.run(); + } + + /** + * Invoke to tell listeners that an atomic test finished. Always invoke + * {@link #fireTestFinished(Description)} if you invoke {@link #fireTestStarted(Description)} + * as listeners are likely to expect them to come in pairs. + * @param description the description of the test that finished + */ + public void fireTestFinished(final Description description) { + new SafeNotifier() { + @Override + protected void notifyListener(RunListener each) throws Exception { + each.testFinished(description); + }; + }.run(); + } + + /** + * Ask that the tests run stop before starting the next test. Phrased politely because + * the test currently running will not be interrupted. It seems a little odd to put this + * functionality here, but the <code>RunNotifier</code> is the only object guaranteed + * to be shared amongst the many runners involved. + */ + public void pleaseStop() { + fPleaseStop= true; + } + + /** + * Internal use only. The Result's listener must be first. + */ + public void addFirstListener(RunListener listener) { + fListeners.add(0, listener); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runner/notification/StoppedByUserException.java b/junit4/src/main/java/org/junit/runner/notification/StoppedByUserException.java new file mode 100644 index 0000000..89be3ba --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/notification/StoppedByUserException.java @@ -0,0 +1,11 @@ +package org.junit.runner.notification; + +/** + * Thrown when a user has requested that the test run stop. Writers of + * test running GUIs should be prepared to catch a <code>StoppedByUserException</code>. + * + * @see org.junit.runner.notification.RunNotifier + */ +public class StoppedByUserException extends RuntimeException { + private static final long serialVersionUID= 1L; +} diff --git a/junit4/src/main/java/org/junit/runner/notification/package-info.java b/junit4/src/main/java/org/junit/runner/notification/package-info.java new file mode 100644 index 0000000..0331c8f --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/notification/package-info.java @@ -0,0 +1,6 @@ +/** + * Provides information about a test run. + * + * @since 4.0 + */ +package org.junit.runner.notification;
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runner/package-info.java b/junit4/src/main/java/org/junit/runner/package-info.java new file mode 100644 index 0000000..e19fa0b --- /dev/null +++ b/junit4/src/main/java/org/junit/runner/package-info.java @@ -0,0 +1,6 @@ +/** + * Provides classes used to describe, collect, run and analyze multiple tests. + * + * @since 4.0 + */ +package org.junit.runner;
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runners/AllTests.java b/junit4/src/main/java/org/junit/runners/AllTests.java new file mode 100644 index 0000000..50c02db --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/AllTests.java @@ -0,0 +1,24 @@ +package org.junit.runners; + +import org.junit.internal.runners.SuiteMethod; + +/** Runner for use with JUnit 3.8.x-style AllTests classes + * (those that only implement a static <code>suite()</code> + * method). For example: + * <pre> + * @RunWith(AllTests.class) + * public class ProductTests { + * public static junit.framework.Test suite() { + * ... + * } + * } + * </pre> + */ +public class AllTests extends SuiteMethod { + /** + * Only called reflectively. Do not use programmatically. + */ + public AllTests(Class<?> klass) throws Throwable { + super(klass); + } +} diff --git a/junit4/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java b/junit4/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java new file mode 100644 index 0000000..92e0d07 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java @@ -0,0 +1,407 @@ +package org.junit.runners; + +import static org.junit.internal.runners.rules.RuleFieldValidator.RULE_VALIDATOR; + +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.Test.None; +import org.junit.internal.runners.model.ReflectiveCallable; +import org.junit.internal.runners.statements.ExpectException; +import org.junit.internal.runners.statements.Fail; +import org.junit.internal.runners.statements.FailOnTimeout; +import org.junit.internal.runners.statements.InvokeMethod; +import org.junit.internal.runners.statements.RunAfters; +import org.junit.internal.runners.statements.RunBefores; +import org.junit.rules.RunRules; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.MultipleFailureException; +import org.junit.runners.model.Statement; + +/** + * Implements the JUnit 4 standard test case class model, as defined by the + * annotations in the org.junit package. Many users will never notice this + * class: it is now the default test class runner, but it should have exactly + * the same behavior as the old test class runner ({@code JUnit4ClassRunner}). + * + * BlockJUnit4ClassRunner has advantages for writers of custom JUnit runners + * that are slight changes to the default behavior, however: + * + * <ul> + * <li>It has a much simpler implementation based on {@link Statement}s, + * allowing new operations to be inserted into the appropriate point in the + * execution flow. + * + * <li>It is published, and extension and reuse are encouraged, whereas {@code + * JUnit4ClassRunner} was in an internal package, and is now deprecated. + * </ul> + */ +public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> { + /** + * Creates a BlockJUnit4ClassRunner to run {@code klass} + * + * @throws InitializationError + * if the test class is malformed. + */ + public BlockJUnit4ClassRunner(Class<?> klass) throws InitializationError { + super(klass); + } + + // + // Implementation of ParentRunner + // + + @Override + protected void runChild(final FrameworkMethod method, RunNotifier notifier) { + Description description= describeChild(method); + if (method.getAnnotation(Ignore.class) != null) { + notifier.fireTestIgnored(description); + } else { + runLeaf(methodBlock(method), description, notifier); + } + } + + @Override + protected Description describeChild(FrameworkMethod method) { + return Description.createTestDescription(getTestClass().getJavaClass(), + testName(method), method.getAnnotations()); + } + + @Override + protected List<FrameworkMethod> getChildren() { + return computeTestMethods(); + } + + // + // Override in subclasses + // + + /** + * Returns the methods that run tests. Default implementation returns all + * methods annotated with {@code @Test} on this class and superclasses that + * are not overridden. + */ + protected List<FrameworkMethod> computeTestMethods() { + return getTestClass().getAnnotatedMethods(Test.class); + } + + @Override + protected void collectInitializationErrors(List<Throwable> errors) { + super.collectInitializationErrors(errors); + + validateNoNonStaticInnerClass(errors); + validateConstructor(errors); + validateInstanceMethods(errors); + validateFields(errors); + } + + protected void validateNoNonStaticInnerClass(List<Throwable> errors) { + if (getTestClass().isANonStaticInnerClass()) { + String gripe= "The inner class " + getTestClass().getName() + + " is not static."; + errors.add(new Exception(gripe)); + } + } + + /** + * Adds to {@code errors} if the test class has more than one constructor, + * or if the constructor takes parameters. Override if a subclass requires + * different validation rules. + */ + protected void validateConstructor(List<Throwable> errors) { + validateOnlyOneConstructor(errors); + validateZeroArgConstructor(errors); + } + + /** + * Adds to {@code errors} if the test class has more than one constructor + * (do not override) + */ + protected void validateOnlyOneConstructor(List<Throwable> errors) { + if (!hasOneConstructor()) { + String gripe= "Test class should have exactly one public constructor"; + errors.add(new Exception(gripe)); + } + } + + /** + * Adds to {@code errors} if the test class's single constructor takes + * parameters (do not override) + */ + protected void validateZeroArgConstructor(List<Throwable> errors) { + if (!getTestClass().isANonStaticInnerClass() + && hasOneConstructor() + && (getTestClass().getOnlyConstructor().getParameterTypes().length != 0)) { + String gripe= "Test class should have exactly one public zero-argument constructor"; + errors.add(new Exception(gripe)); + } + } + + private boolean hasOneConstructor() { + return getTestClass().getJavaClass().getConstructors().length == 1; + } + + /** + * Adds to {@code errors} for each method annotated with {@code @Test}, + * {@code @Before}, or {@code @After} that is not a public, void instance + * method with no arguments. + * + * @deprecated unused API, will go away in future version + */ + @Deprecated + protected void validateInstanceMethods(List<Throwable> errors) { + validatePublicVoidNoArgMethods(After.class, false, errors); + validatePublicVoidNoArgMethods(Before.class, false, errors); + validateTestMethods(errors); + + if (computeTestMethods().size() == 0) + errors.add(new Exception("No runnable methods")); + } + + private void validateFields(List<Throwable> errors) { + RULE_VALIDATOR.validate(getTestClass(), errors); + } + + /** + * Adds to {@code errors} for each method annotated with {@code @Test}that + * is not a public, void instance method with no arguments. + */ + protected void validateTestMethods(List<Throwable> errors) { + validatePublicVoidNoArgMethods(Test.class, false, errors); + } + + /** + * Returns a new fixture for running a test. Default implementation executes + * the test class's no-argument constructor (validation should have ensured + * one exists). + */ + protected Object createTest() throws Exception { + return getTestClass().getOnlyConstructor().newInstance(); + } + + /** + * Returns the name that describes {@code method} for {@link Description}s. + * Default implementation is the method's name + */ + protected String testName(FrameworkMethod method) { + return method.getName(); + } + + /** + * Returns a Statement that, when executed, either returns normally if + * {@code method} passes, or throws an exception if {@code method} fails. + * + * Here is an outline of the default implementation: + * + * <ul> + * <li>Invoke {@code method} on the result of {@code createTest()}, and + * throw any exceptions thrown by either operation. + * <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code + * expecting} attribute, return normally only if the previous step threw an + * exception of the correct type, and throw an exception otherwise. + * <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code + * timeout} attribute, throw an exception if the previous step takes more + * than the specified number of milliseconds. + * <li>ALWAYS run all non-overridden {@code @Before} methods on this class + * and superclasses before any of the previous steps; if any throws an + * Exception, stop execution and pass the exception on. + * <li>ALWAYS run all non-overridden {@code @After} methods on this class + * and superclasses after any of the previous steps; all After methods are + * always executed: exceptions thrown by previous steps are combined, if + * necessary, with exceptions from After methods into a + * {@link MultipleFailureException}. + * <li>ALWAYS allow {@code @Rule} fields to modify the execution of the + * above steps. A {@code Rule} may prevent all execution of the above steps, + * or add additional behavior before and after, or modify thrown exceptions. + * For more information, see {@link TestRule} + * </ul> + * + * This can be overridden in subclasses, either by overriding this method, + * or the implementations creating each sub-statement. + */ + protected Statement methodBlock(FrameworkMethod method) { + Object test; + try { + test= new ReflectiveCallable() { + @Override + protected Object runReflectiveCall() throws Throwable { + return createTest(); + } + }.run(); + } catch (Throwable e) { + return new Fail(e); + } + + Statement statement= methodInvoker(method, test); + statement= possiblyExpectingExceptions(method, test, statement); + statement= withPotentialTimeout(method, test, statement); + statement= withBefores(method, test, statement); + statement= withAfters(method, test, statement); + statement= withRules(method, test, statement); + return statement; + } + + // + // Statement builders + // + + /** + * Returns a {@link Statement} that invokes {@code method} on {@code test} + */ + protected Statement methodInvoker(FrameworkMethod method, Object test) { + return new InvokeMethod(method, test); + } + + /** + * Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation + * has the {@code expecting} attribute, return normally only if {@code next} + * throws an exception of the correct type, and throw an exception + * otherwise. + * + * @deprecated Will be private soon: use Rules instead + */ + @Deprecated + protected Statement possiblyExpectingExceptions(FrameworkMethod method, + Object test, Statement next) { + Test annotation= method.getAnnotation(Test.class); + return expectsException(annotation) ? new ExpectException(next, + getExpectedException(annotation)) : next; + } + + /** + * Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation + * has the {@code timeout} attribute, throw an exception if {@code next} + * takes more than the specified number of milliseconds. + * + * @deprecated Will be private soon: use Rules instead + */ + @Deprecated + protected Statement withPotentialTimeout(FrameworkMethod method, + Object test, Statement next) { + long timeout= getTimeout(method.getAnnotation(Test.class)); + return timeout > 0 ? new FailOnTimeout(next, timeout) : next; + } + + /** + * Returns a {@link Statement}: run all non-overridden {@code @Before} + * methods on this class and superclasses before running {@code next}; if + * any throws an Exception, stop execution and pass the exception on. + * + * @deprecated Will be private soon: use Rules instead + */ + @Deprecated + protected Statement withBefores(FrameworkMethod method, Object target, + Statement statement) { + List<FrameworkMethod> befores= getTestClass().getAnnotatedMethods( + Before.class); + return befores.isEmpty() ? statement : new RunBefores(statement, + befores, target); + } + + /** + * Returns a {@link Statement}: run all non-overridden {@code @After} + * methods on this class and superclasses before running {@code next}; all + * After methods are always executed: exceptions thrown by previous steps + * are combined, if necessary, with exceptions from After methods into a + * {@link MultipleFailureException}. + * + * @deprecated Will be private soon: use Rules instead + */ + @Deprecated + protected Statement withAfters(FrameworkMethod method, Object target, + Statement statement) { + List<FrameworkMethod> afters= getTestClass().getAnnotatedMethods( + After.class); + return afters.isEmpty() ? statement : new RunAfters(statement, afters, + target); + } + + private Statement withRules(FrameworkMethod method, Object target, + Statement statement) { + Statement result= statement; + result= withMethodRules(method, target, result); + result= withTestRules(method, target, result); + return result; + } + + @SuppressWarnings("deprecation") + private Statement withMethodRules(FrameworkMethod method, Object target, + Statement result) { + List<TestRule> testRules= getTestRules(target); + for (org.junit.rules.MethodRule each : getMethodRules(target)) + if (! testRules.contains(each)) + result= each.apply(result, method, target); + return result; + } + + @SuppressWarnings("deprecation") + private List<org.junit.rules.MethodRule> getMethodRules(Object target) { + return rules(target); + } + + /** + * @param target + * the test case instance + * @return a list of MethodRules that should be applied when executing this + * test + * @deprecated {@link org.junit.rules.MethodRule} is a deprecated interface. Port to + * {@link TestRule} and + * {@link BlockJUnit4ClassRunner#getTestRules(Object)} + */ + @Deprecated + protected List<org.junit.rules.MethodRule> rules(Object target) { + return getTestClass().getAnnotatedFieldValues(target, Rule.class, + org.junit.rules.MethodRule.class); + } + + /** + * Returns a {@link Statement}: apply all non-static {@link Value} fields + * annotated with {@link Rule}. + * + * @param statement The base statement + * @return a RunRules statement if any class-level {@link Rule}s are + * found, or the base statement + */ + private Statement withTestRules(FrameworkMethod method, Object target, + Statement statement) { + List<TestRule> testRules= getTestRules(target); + return testRules.isEmpty() ? statement : + new RunRules(statement, testRules, describeChild(method)); + } + + /** + * @param target + * the test case instance + * @return a list of TestRules that should be applied when executing this + * test + */ + protected List<TestRule> getTestRules(Object target) { + return getTestClass().getAnnotatedFieldValues(target, + Rule.class, TestRule.class); + } + + private Class<? extends Throwable> getExpectedException(Test annotation) { + if (annotation == null || annotation.expected() == None.class) + return null; + else + return annotation.expected(); + } + + private boolean expectsException(Test annotation) { + return getExpectedException(annotation) != null; + } + + private long getTimeout(Test annotation) { + if (annotation == null) + return 0; + return annotation.timeout(); + } +} diff --git a/junit4/src/main/java/org/junit/runners/JUnit4.java b/junit4/src/main/java/org/junit/runners/JUnit4.java new file mode 100644 index 0000000..1e1f347 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/JUnit4.java @@ -0,0 +1,22 @@ +package org.junit.runners; + +import org.junit.runners.model.InitializationError; + +/** + * Aliases the current default JUnit 4 class runner, for future-proofing. If + * future versions of JUnit change the default Runner class, they will also + * change the definition of this class. Developers wanting to explicitly tag a + * class as a JUnit 4 class should use {@code @RunWith(JUnit4.class)}, not, + * for example in JUnit 4.5, {@code @RunWith(BlockJUnit4ClassRunner.class)}. + * This is the only way this class should be used--any extension that + * depends on the implementation details of this class is likely to break + * in future versions. + */ +public final class JUnit4 extends BlockJUnit4ClassRunner { + /** + * Constructs a new instance of the default runner + */ + public JUnit4(Class<?> klass) throws InitializationError { + super(klass); + } +} diff --git a/junit4/src/main/java/org/junit/runners/Parameterized.java b/junit4/src/main/java/org/junit/runners/Parameterized.java new file mode 100644 index 0000000..3ebfead --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/Parameterized.java @@ -0,0 +1,167 @@ +package org.junit.runners; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +/** + * <p> + * The custom runner <code>Parameterized</code> implements parameterized tests. + * When running a parameterized test class, instances are created for the + * cross-product of the test methods and the test data elements. + * </p> + * + * For example, to test a Fibonacci function, write: + * + * <pre> + * @RunWith(Parameterized.class) + * public class FibonacciTest { + * @Parameters + * public static List<Object[]> data() { + * return Arrays.asList(new Object[][] { + * { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } + * }); + * } + * + * private int fInput; + * + * private int fExpected; + * + * public FibonacciTest(int input, int expected) { + * fInput= input; + * fExpected= expected; + * } + * + * @Test + * public void test() { + * assertEquals(fExpected, Fibonacci.compute(fInput)); + * } + * } + * </pre> + * + * <p> + * Each instance of <code>FibonacciTest</code> will be constructed using the + * two-argument constructor and the data values in the + * <code>@Parameters</code> method. + * </p> + */ +public class Parameterized extends Suite { + /** + * Annotation for a method which provides parameters to be injected into the + * test class constructor by <code>Parameterized</code> + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public static @interface Parameters { + } + + private class TestClassRunnerForParameters extends + BlockJUnit4ClassRunner { + private final int fParameterSetNumber; + + private final List<Object[]> fParameterList; + + TestClassRunnerForParameters(Class<?> type, + List<Object[]> parameterList, int i) throws InitializationError { + super(type); + fParameterList= parameterList; + fParameterSetNumber= i; + } + + @Override + public Object createTest() throws Exception { + return getTestClass().getOnlyConstructor().newInstance( + computeParams()); + } + + private Object[] computeParams() throws Exception { + try { + return fParameterList.get(fParameterSetNumber); + } catch (ClassCastException e) { + throw new Exception(String.format( + "%s.%s() must return a Collection of arrays.", + getTestClass().getName(), getParametersMethod( + getTestClass()).getName())); + } + } + + @Override + protected String getName() { + return String.format("[%s]", fParameterSetNumber); + } + + @Override + protected String testName(final FrameworkMethod method) { + return String.format("%s[%s]", method.getName(), + fParameterSetNumber); + } + + @Override + protected void validateConstructor(List<Throwable> errors) { + validateOnlyOneConstructor(errors); + } + + @Override + protected Statement classBlock(RunNotifier notifier) { + return childrenInvoker(notifier); + } + + @Override + protected Annotation[] getRunnerAnnotations() { + return new Annotation[0]; + } + } + + private final ArrayList<Runner> runners= new ArrayList<Runner>(); + + /** + * Only called reflectively. Do not use programmatically. + */ + public Parameterized(Class<?> klass) throws Throwable { + super(klass, Collections.<Runner>emptyList()); + List<Object[]> parametersList= getParametersList(getTestClass()); + for (int i= 0; i < parametersList.size(); i++) + runners.add(new TestClassRunnerForParameters(getTestClass().getJavaClass(), + parametersList, i)); + } + + @Override + protected List<Runner> getChildren() { + return runners; + } + + @SuppressWarnings("unchecked") + private List<Object[]> getParametersList(TestClass klass) + throws Throwable { + return (List<Object[]>) getParametersMethod(klass).invokeExplosively( + null); + } + + private FrameworkMethod getParametersMethod(TestClass testClass) + throws Exception { + List<FrameworkMethod> methods= testClass + .getAnnotatedMethods(Parameters.class); + for (FrameworkMethod each : methods) { + int modifiers= each.getMethod().getModifiers(); + if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) + return each; + } + + throw new Exception("No public static parameters method on class " + + testClass.getName()); + } + +} diff --git a/junit4/src/main/java/org/junit/runners/ParentRunner.java b/junit4/src/main/java/org/junit/runners/ParentRunner.java new file mode 100644 index 0000000..a41aad3 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/ParentRunner.java @@ -0,0 +1,378 @@ +package org.junit.runners; + +import static org.junit.internal.runners.rules.RuleFieldValidator.CLASS_RULE_VALIDATOR; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.internal.AssumptionViolatedException; +import org.junit.internal.runners.model.EachTestNotifier; +import org.junit.internal.runners.statements.RunAfters; +import org.junit.internal.runners.statements.RunBefores; +import org.junit.rules.RunRules; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.manipulation.Sortable; +import org.junit.runner.manipulation.Sorter; +import org.junit.runner.notification.RunNotifier; +import org.junit.runner.notification.StoppedByUserException; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.MultipleFailureException; +import org.junit.runners.model.RunnerScheduler; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +/** + * Provides most of the functionality specific to a Runner that implements a + * "parent node" in the test tree, with children defined by objects of some data + * type {@code T}. (For {@link BlockJUnit4ClassRunner}, {@code T} is + * {@link Method} . For {@link Suite}, {@code T} is {@link Class}.) Subclasses + * must implement finding the children of the node, describing each child, and + * running each child. ParentRunner will filter and sort children, handle + * {@code @BeforeClass} and {@code @AfterClass} methods, + * handle annotated {@link ClassRule}s, create a composite + * {@link Description}, and run children sequentially. + */ +public abstract class ParentRunner<T> extends Runner implements Filterable, + Sortable { + private final TestClass fTestClass; + + private Sorter fSorter= Sorter.NULL; + + private List<T> fFilteredChildren= null; + + private RunnerScheduler fScheduler= new RunnerScheduler() { + public void schedule(Runnable childStatement) { + childStatement.run(); + } + + public void finished() { + // do nothing + } + }; + + /** + * Constructs a new {@code ParentRunner} that will run {@code @TestClass} + * @throws InitializationError + */ + protected ParentRunner(Class<?> testClass) throws InitializationError { + fTestClass= new TestClass(testClass); + validate(); + } + + // + // Must be overridden + // + + /** + * Returns a list of objects that define the children of this Runner. + */ + protected abstract List<T> getChildren(); + + /** + * Returns a {@link Description} for {@code child}, which can be assumed to + * be an element of the list returned by {@link ParentRunner#getChildren()} + */ + protected abstract Description describeChild(T child); + + /** + * Runs the test corresponding to {@code child}, which can be assumed to be + * an element of the list returned by {@link ParentRunner#getChildren()}. + * Subclasses are responsible for making sure that relevant test events are + * reported through {@code notifier} + */ + protected abstract void runChild(T child, RunNotifier notifier); + + // + // May be overridden + // + + /** + * Adds to {@code errors} a throwable for each problem noted with the test class (available from {@link #getTestClass()}). + * Default implementation adds an error for each method annotated with + * {@code @BeforeClass} or {@code @AfterClass} that is not + * {@code public static void} with no arguments. + */ + protected void collectInitializationErrors(List<Throwable> errors) { + validatePublicVoidNoArgMethods(BeforeClass.class, true, errors); + validatePublicVoidNoArgMethods(AfterClass.class, true, errors); + validateClassRules(errors); + } + + /** + * Adds to {@code errors} if any method in this class is annotated with + * {@code annotation}, but: + * <ul> + * <li>is not public, or + * <li>takes parameters, or + * <li>returns something other than void, or + * <li>is static (given {@code isStatic is false}), or + * <li>is not static (given {@code isStatic is true}). + */ + protected void validatePublicVoidNoArgMethods(Class<? extends Annotation> annotation, + boolean isStatic, List<Throwable> errors) { + List<FrameworkMethod> methods= getTestClass().getAnnotatedMethods(annotation); + + for (FrameworkMethod eachTestMethod : methods) + eachTestMethod.validatePublicVoidNoArg(isStatic, errors); + } + + private void validateClassRules(List<Throwable> errors) { + CLASS_RULE_VALIDATOR.validate(getTestClass(), errors); + } + + /** + * Constructs a {@code Statement} to run all of the tests in the test class. Override to add pre-/post-processing. + * Here is an outline of the implementation: + * <ul> + * <li>Call {@link #runChild(Object, RunNotifier)} on each object returned by {@link #getChildren()} (subject to any imposed filter and sort).</li> + * <li>ALWAYS run all non-overridden {@code @BeforeClass} methods on this class + * and superclasses before the previous step; if any throws an + * Exception, stop execution and pass the exception on. + * <li>ALWAYS run all non-overridden {@code @AfterClass} methods on this class + * and superclasses before any of the previous steps; all AfterClass methods are + * always executed: exceptions thrown by previous steps are combined, if + * necessary, with exceptions from AfterClass methods into a + * {@link MultipleFailureException}. + * </ul> + * @param notifier + * @return {@code Statement} + */ + protected Statement classBlock(final RunNotifier notifier) { + Statement statement= childrenInvoker(notifier); + statement= withBeforeClasses(statement); + statement= withAfterClasses(statement); + statement= withClassRules(statement); + return statement; + } + + /** + * Returns a {@link Statement}: run all non-overridden {@code @BeforeClass} methods on this class + * and superclasses before executing {@code statement}; if any throws an + * Exception, stop execution and pass the exception on. + */ + protected Statement withBeforeClasses(Statement statement) { + List<FrameworkMethod> befores= fTestClass + .getAnnotatedMethods(BeforeClass.class); + return befores.isEmpty() ? statement : + new RunBefores(statement, befores, null); + } + + /** + * Returns a {@link Statement}: run all non-overridden {@code @AfterClass} methods on this class + * and superclasses before executing {@code statement}; all AfterClass methods are + * always executed: exceptions thrown by previous steps are combined, if + * necessary, with exceptions from AfterClass methods into a + * {@link MultipleFailureException}. + */ + protected Statement withAfterClasses(Statement statement) { + List<FrameworkMethod> afters= fTestClass + .getAnnotatedMethods(AfterClass.class); + return afters.isEmpty() ? statement : + new RunAfters(statement, afters, null); + } + + /** + * Returns a {@link Statement}: apply all + * static fields assignable to {@link TestRule} + * annotated with {@link ClassRule}. + * + * @param statement + * the base statement + * @return a RunRules statement if any class-level {@link Rule}s are + * found, or the base statement + */ + private Statement withClassRules(Statement statement) { + List<TestRule> classRules= classRules(); + return classRules.isEmpty() ? statement : + new RunRules(statement, classRules, getDescription()); + } + + /** + * @return the {@code ClassRule}s that can transform the block that runs + * each method in the tested class. + */ + protected List<TestRule> classRules() { + return fTestClass.getAnnotatedFieldValues(null, ClassRule.class, TestRule.class); + } + + /** + * Returns a {@link Statement}: Call {@link #runChild(Object, RunNotifier)} + * on each object returned by {@link #getChildren()} (subject to any imposed + * filter and sort) + */ + protected Statement childrenInvoker(final RunNotifier notifier) { + return new Statement() { + @Override + public void evaluate() { + runChildren(notifier); + } + }; + } + + private void runChildren(final RunNotifier notifier) { + for (final T each : getFilteredChildren()) + fScheduler.schedule(new Runnable() { + public void run() { + ParentRunner.this.runChild(each, notifier); + } + }); + fScheduler.finished(); + } + + /** + * Returns a name used to describe this Runner + */ + protected String getName() { + return fTestClass.getName(); + } + + // + // Available for subclasses + // + + /** + * Returns a {@link TestClass} object wrapping the class to be executed. + */ + public final TestClass getTestClass() { + return fTestClass; + } + + /** + * Runs a {@link Statement} that represents a leaf (aka atomic) test. + */ + protected final void runLeaf(Statement statement, Description description, + RunNotifier notifier) { + EachTestNotifier eachNotifier= new EachTestNotifier(notifier, description); + eachNotifier.fireTestStarted(); + try { + statement.evaluate(); + } catch (AssumptionViolatedException e) { + eachNotifier.addFailedAssumption(e); + } catch (Throwable e) { + eachNotifier.addFailure(e); + } finally { + eachNotifier.fireTestFinished(); + } + } + + /** + * @return the annotations that should be attached to this runner's + * description. + */ + protected Annotation[] getRunnerAnnotations() { + return fTestClass.getAnnotations(); + } + + // + // Implementation of Runner + // + + @Override + public Description getDescription() { + Description description= Description.createSuiteDescription(getName(), + getRunnerAnnotations()); + for (T child : getFilteredChildren()) + description.addChild(describeChild(child)); + return description; + } + + @Override + public void run(final RunNotifier notifier) { + EachTestNotifier testNotifier= new EachTestNotifier(notifier, + getDescription()); + try { + Statement statement= classBlock(notifier); + statement.evaluate(); + } catch (AssumptionViolatedException e) { + testNotifier.fireTestIgnored(); + } catch (StoppedByUserException e) { + throw e; + } catch (Throwable e) { + testNotifier.addFailure(e); + } + } + + // + // Implementation of Filterable and Sortable + // + + public void filter(Filter filter) throws NoTestsRemainException { + for (Iterator<T> iter = getFilteredChildren().iterator(); iter.hasNext(); ) { + T each = iter.next(); + if (shouldRun(filter, each)) + try { + filter.apply(each); + } catch (NoTestsRemainException e) { + iter.remove(); + } + else + iter.remove(); + } + if (getFilteredChildren().isEmpty()) { + throw new NoTestsRemainException(); + } + } + + public void sort(Sorter sorter) { + fSorter= sorter; + for (T each : getFilteredChildren()) + sortChild(each); + Collections.sort(getFilteredChildren(), comparator()); + } + + // + // Private implementation + // + + private void validate() throws InitializationError { + List<Throwable> errors= new ArrayList<Throwable>(); + collectInitializationErrors(errors); + if (!errors.isEmpty()) + throw new InitializationError(errors); + } + + private List<T> getFilteredChildren() { + if (fFilteredChildren == null) + fFilteredChildren = new ArrayList<T>(getChildren()); + return fFilteredChildren; + } + + private void sortChild(T child) { + fSorter.apply(child); + } + + private boolean shouldRun(Filter filter, T each) { + return filter.shouldRun(describeChild(each)); + } + + private Comparator<? super T> comparator() { + return new Comparator<T>() { + public int compare(T o1, T o2) { + return fSorter.compare(describeChild(o1), describeChild(o2)); + } + }; + } + + /** + * Sets a scheduler that determines the order and parallelization + * of children. Highly experimental feature that may change. + */ + public void setScheduler(RunnerScheduler scheduler) { + this.fScheduler = scheduler; + } +} diff --git a/junit4/src/main/java/org/junit/runners/Suite.java b/junit4/src/main/java/org/junit/runners/Suite.java new file mode 100644 index 0000000..1b3bb48 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/Suite.java @@ -0,0 +1,130 @@ +package org.junit.runners; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; + +import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +/** + * Using <code>Suite</code> as a runner allows you to manually + * build a suite containing tests from many classes. It is the JUnit 4 equivalent of the JUnit 3.8.x + * static {@link junit.framework.Test} <code>suite()</code> method. To use it, annotate a class + * with <code>@RunWith(Suite.class)</code> and <code>@SuiteClasses({TestClass1.class, ...})</code>. + * When you run this class, it will run all the tests in all the suite classes. + */ +public class Suite extends ParentRunner<Runner> { + /** + * Returns an empty suite. + */ + public static Runner emptySuite() { + try { + return new Suite((Class<?>)null, new Class<?>[0]); + } catch (InitializationError e) { + throw new RuntimeException("This shouldn't be possible"); + } + } + + /** + * The <code>SuiteClasses</code> annotation specifies the classes to be run when a class + * annotated with <code>@RunWith(Suite.class)</code> is run. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + @Inherited + public @interface SuiteClasses { + /** + * @return the classes to be run + */ + public Class<?>[] value(); + } + + private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError { + SuiteClasses annotation= klass.getAnnotation(SuiteClasses.class); + if (annotation == null) + throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName())); + return annotation.value(); + } + + private final List<Runner> fRunners; + + /** + * Called reflectively on classes annotated with <code>@RunWith(Suite.class)</code> + * + * @param klass the root class + * @param builder builds runners for classes in the suite + * @throws InitializationError + */ + public Suite(Class<?> klass, RunnerBuilder builder) throws InitializationError { + this(builder, klass, getAnnotatedClasses(klass)); + } + + /** + * Call this when there is no single root class (for example, multiple class names + * passed on the command line to {@link org.junit.runner.JUnitCore} + * + * @param builder builds runners for classes in the suite + * @param classes the classes in the suite + * @throws InitializationError + */ + public Suite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError { + this(null, builder.runners(null, classes)); + } + + /** + * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4. + * @param klass the root of the suite + * @param suiteClasses the classes in the suite + * @throws InitializationError + */ + protected Suite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError { + this(new AllDefaultPossibilitiesBuilder(true), klass, suiteClasses); + } + + /** + * Called by this class and subclasses once the classes making up the suite have been determined + * + * @param builder builds runners for classes in the suite + * @param klass the root of the suite + * @param suiteClasses the classes in the suite + * @throws InitializationError + */ + protected Suite(RunnerBuilder builder, Class<?> klass, Class<?>[] suiteClasses) throws InitializationError { + this(klass, builder.runners(klass, suiteClasses)); + } + + /** + * Called by this class and subclasses once the runners making up the suite have been determined + * + * @param klass root of the suite + * @param runners for each class in the suite, a {@link Runner} + * @throws InitializationError + */ + protected Suite(Class<?> klass, List<Runner> runners) throws InitializationError { + super(klass); + fRunners = runners; + } + + @Override + protected List<Runner> getChildren() { + return fRunners; + } + + @Override + protected Description describeChild(Runner child) { + return child.getDescription(); + } + + @Override + protected void runChild(Runner runner, final RunNotifier notifier) { + runner.run(notifier); + } +} diff --git a/junit4/src/main/java/org/junit/runners/model/FrameworkField.java b/junit4/src/main/java/org/junit/runners/model/FrameworkField.java new file mode 100644 index 0000000..4a4d4a4 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/FrameworkField.java @@ -0,0 +1,65 @@ +package org.junit.runners.model; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * Represents a field on a test class (currently used only for Rules in + * {@link BlockJUnit4ClassRunner}, but custom runners can make other uses) + */ +public class FrameworkField extends FrameworkMember<FrameworkField> { + private final Field fField; + + FrameworkField(Field field) { + fField= field; + } + + public String getName() { + return getField().getName(); + } + + @Override + public Annotation[] getAnnotations() { + return fField.getAnnotations(); + } + + public boolean isPublic() { + int modifiers= fField.getModifiers(); + return Modifier.isPublic(modifiers); + } + + @Override + public boolean isShadowedBy(FrameworkField otherMember) { + return otherMember.getName().equals(getName()); + } + + public boolean isStatic() { + int modifiers= fField.getModifiers(); + return Modifier.isStatic(modifiers); + } + + /** + * @return the underlying java Field + */ + public Field getField() { + return fField; + } + + /** + * @return the underlying Java Field type + * @see java.lang.reflect.Field#getType() + */ + public Class<?> getType() { + return fField.getType(); + } + + /** + * Attempts to retrieve the value of this field on {@code target} + */ + public Object get(Object target) throws IllegalArgumentException, IllegalAccessException { + return fField.get(target); + } +} diff --git a/junit4/src/main/java/org/junit/runners/model/FrameworkMember.java b/junit4/src/main/java/org/junit/runners/model/FrameworkMember.java new file mode 100644 index 0000000..9cccd4b --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/FrameworkMember.java @@ -0,0 +1,20 @@ +package org.junit.runners.model; + +import java.lang.annotation.Annotation; +import java.util.List; + +abstract class FrameworkMember<T extends FrameworkMember<T>> { + /** + * Returns the annotations on this method + */ + abstract Annotation[] getAnnotations(); + + abstract boolean isShadowedBy(T otherMember); + + boolean isShadowedBy(List<T> members) { + for (T each : members) + if (isShadowedBy(each)) + return true; + return false; + } +} diff --git a/junit4/src/main/java/org/junit/runners/model/FrameworkMethod.java b/junit4/src/main/java/org/junit/runners/model/FrameworkMethod.java new file mode 100644 index 0000000..81c8963 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/FrameworkMethod.java @@ -0,0 +1,156 @@ +package org.junit.runners.model; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.List; + +import org.junit.internal.runners.model.ReflectiveCallable; + +/** + * Represents a method on a test class to be invoked at the appropriate point in + * test execution. These methods are usually marked with an annotation (such as + * {@code @Test}, {@code @Before}, {@code @After}, {@code @BeforeClass}, + * {@code @AfterClass}, etc.) + */ +public class FrameworkMethod extends FrameworkMember<FrameworkMethod> { + final Method fMethod; + + /** + * Returns a new {@code FrameworkMethod} for {@code method} + */ + public FrameworkMethod(Method method) { + fMethod= method; + } + + /** + * Returns the underlying Java method + */ + public Method getMethod() { + return fMethod; + } + + /** + * Returns the result of invoking this method on {@code target} with + * parameters {@code params}. {@link InvocationTargetException}s thrown are + * unwrapped, and their causes rethrown. + */ + public Object invokeExplosively(final Object target, final Object... params) + throws Throwable { + return new ReflectiveCallable() { + @Override + protected Object runReflectiveCall() throws Throwable { + return fMethod.invoke(target, params); + } + }.run(); + } + + /** + * Returns the method's name + */ + public String getName() { + return fMethod.getName(); + } + + /** + * Adds to {@code errors} if this method: + * <ul> + * <li>is not public, or + * <li>takes parameters, or + * <li>returns something other than void, or + * <li>is static (given {@code isStatic is false}), or + * <li>is not static (given {@code isStatic is true}). + */ + public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) { + validatePublicVoid(isStatic, errors); + if (fMethod.getParameterTypes().length != 0) + errors.add(new Exception("Method " + fMethod.getName() + " should have no parameters")); + } + + + /** + * Adds to {@code errors} if this method: + * <ul> + * <li>is not public, or + * <li>returns something other than void, or + * <li>is static (given {@code isStatic is false}), or + * <li>is not static (given {@code isStatic is true}). + */ + public void validatePublicVoid(boolean isStatic, List<Throwable> errors) { + if (Modifier.isStatic(fMethod.getModifiers()) != isStatic) { + String state= isStatic ? "should" : "should not"; + errors.add(new Exception("Method " + fMethod.getName() + "() " + state + " be static")); + } + if (!Modifier.isPublic(fMethod.getDeclaringClass().getModifiers())) + errors.add(new Exception("Class " + fMethod.getDeclaringClass().getName() + " should be public")); + if (!Modifier.isPublic(fMethod.getModifiers())) + errors.add(new Exception("Method " + fMethod.getName() + "() should be public")); + if (fMethod.getReturnType() != Void.TYPE) + errors.add(new Exception("Method " + fMethod.getName() + "() should be void")); + } + + public void validateNoTypeParametersOnArgs(List<Throwable> errors) { + new NoGenericTypeParametersValidator(fMethod).validate(errors); + } + + @Override + public boolean isShadowedBy(FrameworkMethod other) { + if (!other.getName().equals(getName())) + return false; + if (other.getParameterTypes().length != getParameterTypes().length) + return false; + for (int i= 0; i < other.getParameterTypes().length; i++) + if (!other.getParameterTypes()[i].equals(getParameterTypes()[i])) + return false; + return true; + } + + @Override + public boolean equals(Object obj) { + if (!FrameworkMethod.class.isInstance(obj)) + return false; + return ((FrameworkMethod) obj).fMethod.equals(fMethod); + } + + @Override + public int hashCode() { + return fMethod.hashCode(); + } + + /** + * Returns true iff this is a no-arg method that returns a value assignable + * to {@code type} + * + * @deprecated This is used only by the Theories runner, and does not + * use all the generic type info that it ought to. It will be replaced + * with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod) + * once Theories moves to junit-contrib. + */ + @Deprecated + public boolean producesType(Type type) { + return getParameterTypes().length == 0 && type instanceof Class<?> + && ((Class<?>) type).isAssignableFrom(fMethod.getReturnType()); + } + + private Class<?>[] getParameterTypes() { + return fMethod.getParameterTypes(); + } + + /** + * Returns the annotations on this method + */ + @Override + public Annotation[] getAnnotations() { + return fMethod.getAnnotations(); + } + + /** + * Returns the annotation of type {@code annotationType} on this method, if + * one exists. + */ + public <T extends Annotation> T getAnnotation(Class<T> annotationType) { + return fMethod.getAnnotation(annotationType); + } +} diff --git a/junit4/src/main/java/org/junit/runners/model/InitializationError.java b/junit4/src/main/java/org/junit/runners/model/InitializationError.java new file mode 100644 index 0000000..4de9ea7 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/InitializationError.java @@ -0,0 +1,39 @@ +package org.junit.runners.model; + +import java.util.Arrays; +import java.util.List; + +/** + * Represents one or more problems encountered while initializing a Runner + */ +public class InitializationError extends Exception { + private static final long serialVersionUID= 1L; + private final List<Throwable> fErrors; + + /** + * Construct a new {@code InitializationError} with one or more + * errors {@code errors} as causes + */ + public InitializationError(List<Throwable> errors) { + fErrors= errors; + } + + public InitializationError(Throwable error) { + this(Arrays.asList(error)); + } + + /** + * Construct a new {@code InitializationError} with one cause + * with message {@code string} + */ + public InitializationError(String string) { + this(new Exception(string)); + } + + /** + * Returns one or more Throwables that led to this initialization error. + */ + public List<Throwable> getCauses() { + return fErrors; + } +} diff --git a/junit4/src/main/java/org/junit/runners/model/MultipleFailureException.java b/junit4/src/main/java/org/junit/runners/model/MultipleFailureException.java new file mode 100644 index 0000000..6d70ca0 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/MultipleFailureException.java @@ -0,0 +1,60 @@ +// Copyright 2010 Google Inc. All Rights Reserved. + +package org.junit.runners.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Collects multiple {@code Throwable}s into one exception. + */ +public class MultipleFailureException extends Exception { + private static final long serialVersionUID= 1L; + + private final List<Throwable> fErrors; + + public MultipleFailureException(List<Throwable> errors) { + fErrors= new ArrayList<Throwable>(errors); + } + + public List<Throwable> getFailures() { + return Collections.unmodifiableList(fErrors); + } + + @Override + public String getMessage() { + StringBuilder sb = new StringBuilder( + String.format("There were %d errors:", fErrors.size())); + for (Throwable e : fErrors) { + sb.append(String.format("\n %s(%s)", e.getClass().getName(), e.getMessage())); + } + return sb.toString(); + } + + /** + * Asserts that a list of throwables is empty. If it isn't empty, + * will throw {@link MultipleFailureException} (if there are + * multiple throwables in the list) or the first element in the list + * (if there is only one element). + * + * @param errors list to check + * @throws Throwable if the list is not empty + */ + @SuppressWarnings("deprecation") + public static void assertEmpty(List<Throwable> errors) throws Throwable { + if (errors.isEmpty()) + return; + if (errors.size() == 1) + throw errors.get(0); + + /* + * Many places in the code are documented to throw + * org.junit.internal.runners.model.MultipleFailureException. + * That class now extends this one, so we throw the internal + * exception in case developers have tests that catch + * MultipleFailureException. + */ + throw new org.junit.internal.runners.model.MultipleFailureException(errors); + } +} diff --git a/junit4/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java b/junit4/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java new file mode 100644 index 0000000..77662b8 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java @@ -0,0 +1,53 @@ +package org.junit.runners.model; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.List; + +class NoGenericTypeParametersValidator { + private final Method fMethod; + + NoGenericTypeParametersValidator(Method method) { + this.fMethod = method; + } + + void validate(List<Throwable> errors) { + for (Type each : fMethod.getGenericParameterTypes()) + validateNoTypeParameterOnType(each, errors); + } + + private void validateNoTypeParameterOnType(Type type, List<Throwable> errors) { + if (type instanceof TypeVariable<?>) { + errors.add(new Exception("Method " + fMethod.getName() + + "() contains unresolved type variable " + type)); + } else if (type instanceof ParameterizedType) + validateNoTypeParameterOnParameterizedType((ParameterizedType) type, errors); + else if (type instanceof WildcardType) + validateNoTypeParameterOnWildcardType((WildcardType) type, errors); + else if (type instanceof GenericArrayType) + validateNoTypeParameterOnGenericArrayType((GenericArrayType) type, errors); + } + + private void validateNoTypeParameterOnParameterizedType(ParameterizedType parameterized, + List<Throwable> errors) { + for (Type each : parameterized.getActualTypeArguments()) + validateNoTypeParameterOnType(each, errors); + } + + private void validateNoTypeParameterOnWildcardType(WildcardType wildcard, + List<Throwable> errors) { + for (Type each : wildcard.getUpperBounds()) + validateNoTypeParameterOnType(each, errors); + for (Type each : wildcard.getLowerBounds()) + validateNoTypeParameterOnType(each, errors); + } + + private void validateNoTypeParameterOnGenericArrayType( + GenericArrayType arrayType, List<Throwable> errors) { + validateNoTypeParameterOnType(arrayType.getGenericComponentType(), errors); + } +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runners/model/RunnerBuilder.java b/junit4/src/main/java/org/junit/runners/model/RunnerBuilder.java new file mode 100644 index 0000000..3a334be --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/RunnerBuilder.java @@ -0,0 +1,104 @@ +package org.junit.runners.model; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.internal.runners.ErrorReportingRunner; +import org.junit.runner.Runner; + +/** + * A RunnerBuilder is a strategy for constructing runners for classes. + * + * Only writers of custom runners should use <code>RunnerBuilder</code>s. A custom runner class with a constructor taking + * a <code>RunnerBuilder</code> parameter will be passed the instance of <code>RunnerBuilder</code> used to build that runner itself. + * For example, + * imagine a custom runner that builds suites based on a list of classes in a text file: + * + * <pre> + * \@RunWith(TextFileSuite.class) + * \@SuiteSpecFile("mysuite.txt") + * class MySuite {} + * </pre> + * + * The implementation of TextFileSuite might include: + * + * <pre> + * public TextFileSuite(Class testClass, RunnerBuilder builder) { + * // ... + * for (String className : readClassNames()) + * addRunner(builder.runnerForClass(Class.forName(className))); + * // ... + * } + * </pre> + * + * @see org.junit.runners.Suite + */ +public abstract class RunnerBuilder { + private final Set<Class<?>> parents= new HashSet<Class<?>>(); + + /** + * Override to calculate the correct runner for a test class at runtime. + * + * @param testClass class to be run + * @return a Runner + * @throws Throwable if a runner cannot be constructed + */ + public abstract Runner runnerForClass(Class<?> testClass) throws Throwable; + + /** + * Always returns a runner, even if it is just one that prints an error instead of running tests. + * @param testClass class to be run + * @return a Runner + */ + public Runner safeRunnerForClass(Class<?> testClass) { + try { + return runnerForClass(testClass); + } catch (Throwable e) { + return new ErrorReportingRunner(testClass, e); + } + } + + Class<?> addParent(Class<?> parent) throws InitializationError { + if (!parents.add(parent)) + throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName())); + return parent; + } + + void removeParent(Class<?> klass) { + parents.remove(klass); + } + + /** + * Constructs and returns a list of Runners, one for each child class in + * {@code children}. Care is taken to avoid infinite recursion: + * this builder will throw an exception if it is requested for another + * runner for {@code parent} before this call completes. + */ + public List<Runner> runners(Class<?> parent, Class<?>[] children) + throws InitializationError { + addParent(parent); + + try { + return runners(children); + } finally { + removeParent(parent); + } + } + + public List<Runner> runners(Class<?> parent, List<Class<?>> children) + throws InitializationError { + return runners(parent, children.toArray(new Class<?>[0])); + } + + private List<Runner> runners(Class<?>[] children) { + ArrayList<Runner> runners= new ArrayList<Runner>(); + for (Class<?> each : children) { + Runner childRunner= safeRunnerForClass(each); + if (childRunner != null) + runners.add(childRunner); + } + return runners; + } +} diff --git a/junit4/src/main/java/org/junit/runners/model/RunnerScheduler.java b/junit4/src/main/java/org/junit/runners/model/RunnerScheduler.java new file mode 100644 index 0000000..fbc25a4 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/RunnerScheduler.java @@ -0,0 +1,21 @@ +package org.junit.runners.model; + +/** + * Represents a strategy for scheduling when individual test methods + * should be run (in serial or parallel) + * + * WARNING: still experimental, may go away. + */ +public interface RunnerScheduler { + /** + * Schedule a child statement to run + */ + void schedule(Runnable childStatement); + + /** + * Override to implement any behavior that must occur + * after all children have been scheduled (for example, + * waiting for them all to finish) + */ + void finished(); +} diff --git a/junit4/src/main/java/org/junit/runners/model/Statement.java b/junit4/src/main/java/org/junit/runners/model/Statement.java new file mode 100644 index 0000000..a7c5478 --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/Statement.java @@ -0,0 +1,16 @@ +/** + * + */ +package org.junit.runners.model; + + +/** + * Represents one or more actions to be taken at runtime in the course + * of running a JUnit test suite. + */ +public abstract class Statement { + /** + * Run the action, throwing a {@code Throwable} if anything goes wrong. + */ + public abstract void evaluate() throws Throwable; +}
\ No newline at end of file diff --git a/junit4/src/main/java/org/junit/runners/model/TestClass.java b/junit4/src/main/java/org/junit/runners/model/TestClass.java new file mode 100644 index 0000000..362a13a --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/model/TestClass.java @@ -0,0 +1,159 @@ +package org.junit.runners.model; + +import static java.lang.reflect.Modifier.isStatic; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * Wraps a class to be run, providing method validation and annotation searching + */ +public class TestClass { + private final Class<?> fClass; + + private Map<Class<?>, List<FrameworkMethod>> fMethodsForAnnotations= new HashMap<Class<?>, List<FrameworkMethod>>(); + + private Map<Class<?>, List<FrameworkField>> fFieldsForAnnotations= new HashMap<Class<?>, List<FrameworkField>>(); + + /** + * Creates a {@code TestClass} wrapping {@code klass}. Each time this + * constructor executes, the class is scanned for annotations, which can be + * an expensive process (we hope in future JDK's it will not be.) Therefore, + * try to share instances of {@code TestClass} where possible. + */ + public TestClass(Class<?> klass) { + fClass= klass; + if (klass != null && klass.getConstructors().length > 1) + throw new IllegalArgumentException( + "Test class can only have one constructor"); + + for (Class<?> eachClass : getSuperClasses(fClass)) { + for (Method eachMethod : eachClass.getDeclaredMethods()) + addToAnnotationLists(new FrameworkMethod(eachMethod), + fMethodsForAnnotations); + for (Field eachField : eachClass.getDeclaredFields()) + addToAnnotationLists(new FrameworkField(eachField), + fFieldsForAnnotations); + } + } + + private <T extends FrameworkMember<T>> void addToAnnotationLists(T member, + Map<Class<?>, List<T>> map) { + for (Annotation each : member.getAnnotations()) { + Class<? extends Annotation> type= each.annotationType(); + List<T> members= getAnnotatedMembers(map, type); + if (member.isShadowedBy(members)) + return; + if (runsTopToBottom(type)) + members.add(0, member); + else + members.add(member); + } + } + + /** + * Returns, efficiently, all the non-overridden methods in this class and + * its superclasses that are annotated with {@code annotationClass}. + */ + public List<FrameworkMethod> getAnnotatedMethods( + Class<? extends Annotation> annotationClass) { + return getAnnotatedMembers(fMethodsForAnnotations, annotationClass); + } + + /** + * Returns, efficiently, all the non-overridden fields in this class and its + * superclasses that are annotated with {@code annotationClass}. + */ + public List<FrameworkField> getAnnotatedFields( + Class<? extends Annotation> annotationClass) { + return getAnnotatedMembers(fFieldsForAnnotations, annotationClass); + } + + private <T> List<T> getAnnotatedMembers(Map<Class<?>, List<T>> map, + Class<? extends Annotation> type) { + if (!map.containsKey(type)) + map.put(type, new ArrayList<T>()); + return map.get(type); + } + + private boolean runsTopToBottom(Class<? extends Annotation> annotation) { + return annotation.equals(Before.class) + || annotation.equals(BeforeClass.class); + } + + private List<Class<?>> getSuperClasses(Class<?> testClass) { + ArrayList<Class<?>> results= new ArrayList<Class<?>>(); + Class<?> current= testClass; + while (current != null) { + results.add(current); + current= current.getSuperclass(); + } + return results; + } + + /** + * Returns the underlying Java class. + */ + public Class<?> getJavaClass() { + return fClass; + } + + /** + * Returns the class's name. + */ + public String getName() { + if (fClass == null) + return "null"; + return fClass.getName(); + } + + /** + * Returns the only public constructor in the class, or throws an {@code + * AssertionError} if there are more or less than one. + */ + + public Constructor<?> getOnlyConstructor() { + Constructor<?>[] constructors= fClass.getConstructors(); + Assert.assertEquals(1, constructors.length); + return constructors[0]; + } + + /** + * Returns the annotations on this class + */ + public Annotation[] getAnnotations() { + if (fClass == null) + return new Annotation[0]; + return fClass.getAnnotations(); + } + + public <T> List<T> getAnnotatedFieldValues(Object test, + Class<? extends Annotation> annotationClass, Class<T> valueClass) { + List<T> results= new ArrayList<T>(); + for (FrameworkField each : getAnnotatedFields(annotationClass)) { + try { + Object fieldValue= each.get(test); + if (valueClass.isInstance(fieldValue)) + results.add(valueClass.cast(fieldValue)); + } catch (IllegalAccessException e) { + throw new RuntimeException( + "How did getFields return a field we couldn't access?", e); + } + } + return results; + } + + public boolean isANonStaticInnerClass() { + return fClass.isMemberClass() && !isStatic(fClass.getModifiers()); + } +} diff --git a/junit4/src/main/java/org/junit/runners/package-info.java b/junit4/src/main/java/org/junit/runners/package-info.java new file mode 100644 index 0000000..418acaf --- /dev/null +++ b/junit4/src/main/java/org/junit/runners/package-info.java @@ -0,0 +1,8 @@ +/** + * Provides standard {@link org.junit.runner.Runner Runner} implementations. + * + * @since 4.0 + * @see org.junit.runner.Runner + * @see org.junit.runners.BlockJUnit4ClassRunner + */ +package org.junit.runners;
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/AllTests.java b/junit4/src/test/java/junit/samples/AllTests.java new file mode 100644 index 0000000..2fb7628 --- /dev/null +++ b/junit4/src/test/java/junit/samples/AllTests.java @@ -0,0 +1,22 @@ +package junit.samples; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * TestSuite that runs all the sample tests + * + */ +public class AllTests { + + public static void main (String[] args) { + junit.textui.TestRunner.run (suite()); + } + public static Test suite ( ) { + TestSuite suite= new TestSuite("All JUnit Tests"); + suite.addTest(ListTest.suite()); + suite.addTest(new TestSuite(junit.samples.money.MoneyTest.class)); + suite.addTest(junit.tests.AllTests.suite()); + return suite; + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/ListTest.java b/junit4/src/test/java/junit/samples/ListTest.java new file mode 100644 index 0000000..eac850d --- /dev/null +++ b/junit4/src/test/java/junit/samples/ListTest.java @@ -0,0 +1,63 @@ +package junit.samples; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * A sample test case, testing {@link java.util.Vector}. + * + */ +public class ListTest extends TestCase { + protected List<Integer> fEmpty; + protected List<Integer> fFull; + + public static void main (String[] args) { + junit.textui.TestRunner.run (suite()); + } + @Override + protected void setUp() { + fEmpty= new ArrayList<Integer>(); + fFull= new ArrayList<Integer>(); + fFull.add(1); + fFull.add(2); + fFull.add(3); + } + public static Test suite() { + return new TestSuite(ListTest.class); + } + public void testCapacity() { + int size= fFull.size(); + for (int i= 0; i < 100; i++) + fFull.add(new Integer(i)); + assertTrue(fFull.size() == 100+size); + } + public void testContains() { + assertTrue(fFull.contains(1)); + assertTrue(!fEmpty.contains(1)); + } + public void testElementAt() { + int i= fFull.get(0); + assertTrue(i == 1); + + try { + fFull.get(fFull.size()); + } catch (IndexOutOfBoundsException e) { + return; + } + fail("Should raise an ArrayIndexOutOfBoundsException"); + } + public void testRemoveAll() { + fFull.removeAll(fFull); + fEmpty.removeAll(fEmpty); + assertTrue(fFull.isEmpty()); + assertTrue(fEmpty.isEmpty()); + } + public void testRemoveElement() { + fFull.remove(new Integer(3)); + assertTrue(!fFull.contains(3) ); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/SimpleTest.java b/junit4/src/test/java/junit/samples/SimpleTest.java new file mode 100644 index 0000000..f745021 --- /dev/null +++ b/junit4/src/test/java/junit/samples/SimpleTest.java @@ -0,0 +1,68 @@ +package junit.samples; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Some simple tests. + * + */ +public class SimpleTest extends TestCase { + protected int fValue1; + protected int fValue2; + + @Override + protected void setUp() { + fValue1= 2; + fValue2= 3; + } + public static Test suite() { + + /* + * the type safe way + * + TestSuite suite= new TestSuite(); + suite.addTest( + new SimpleTest("add") { + protected void runTest() { testAdd(); } + } + ); + + suite.addTest( + new SimpleTest("testDivideByZero") { + protected void runTest() { testDivideByZero(); } + } + ); + return suite; + */ + + /* + * the dynamic way + */ + return new TestSuite(SimpleTest.class); + } + public void testAdd() { + double result= fValue1 + fValue2; + // forced failure result == 5 + assertTrue(result == 6); + } + + public int unused; + public void testDivideByZero() { + int zero= 0; + int result= 8/zero; + unused= result; // avoid warning for not using result + } + public void testEquals() { + assertEquals(12, 12); + assertEquals(12L, 12L); + assertEquals(new Long(12), new Long(12)); + + assertEquals("Size", 12, 13); + assertEquals("Capacity", 12.0, 11.99, 0.0); + } + public static void main (String[] args) { + junit.textui.TestRunner.run(suite()); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/money/IMoney.java b/junit4/src/test/java/junit/samples/money/IMoney.java new file mode 100644 index 0000000..b8f9496 --- /dev/null +++ b/junit4/src/test/java/junit/samples/money/IMoney.java @@ -0,0 +1,45 @@ +package junit.samples.money; + +/** + * The common interface for simple Monies and MoneyBags + * + */ +public interface IMoney { + /** + * Adds a money to this money. + */ + public abstract IMoney add(IMoney m); + /** + * Adds a simple Money to this money. This is a helper method for + * implementing double dispatch + */ + public abstract IMoney addMoney(Money m); + /** + * Adds a MoneyBag to this money. This is a helper method for + * implementing double dispatch + */ + public abstract IMoney addMoneyBag(MoneyBag s); + /** + * Tests whether this money is zero + */ + public abstract boolean isZero(); + /** + * Multiplies a money by the given factor. + */ + public abstract IMoney multiply(int factor); + /** + * Negates this money. + */ + public abstract IMoney negate(); + /** + * Subtracts a money from this money. + */ + public abstract IMoney subtract(IMoney m); + /** + * Append this to a MoneyBag m. + * appendTo() needs to be public because it is used + * polymorphically, but it should not be used by clients + * because it modifies the argument m. + */ + public abstract void appendTo(MoneyBag m); +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/money/Money.java b/junit4/src/test/java/junit/samples/money/Money.java new file mode 100644 index 0000000..3d8c2fd --- /dev/null +++ b/junit4/src/test/java/junit/samples/money/Money.java @@ -0,0 +1,78 @@ +package junit.samples.money; + +/** + * A simple Money. + * + */ +public class Money implements IMoney { + + private int fAmount; + private String fCurrency; + + /** + * Constructs a money from the given amount and currency. + */ + public Money(int amount, String currency) { + fAmount= amount; + fCurrency= currency; + } + /** + * Adds a money to this money. Forwards the request to the addMoney helper. + */ + public IMoney add(IMoney m) { + return m.addMoney(this); + } + public IMoney addMoney(Money m) { + if (m.currency().equals(currency()) ) + return new Money(amount()+m.amount(), currency()); + return MoneyBag.create(this, m); + } + public IMoney addMoneyBag(MoneyBag s) { + return s.addMoney(this); + } + public int amount() { + return fAmount; + } + public String currency() { + return fCurrency; + } + @Override + public boolean equals(Object anObject) { + if (isZero()) + if (anObject instanceof IMoney) + return ((IMoney)anObject).isZero(); + if (anObject instanceof Money) { + Money aMoney= (Money)anObject; + return aMoney.currency().equals(currency()) + && amount() == aMoney.amount(); + } + return false; + } + @Override + public int hashCode() { + if (fAmount == 0) + return 0; + return fCurrency.hashCode()+fAmount; + } + public boolean isZero() { + return amount() == 0; + } + public IMoney multiply(int factor) { + return new Money(amount()*factor, currency()); + } + public IMoney negate() { + return new Money(-amount(), currency()); + } + public IMoney subtract(IMoney m) { + return add(m.negate()); + } + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("["+amount()+" "+currency()+"]"); + return buffer.toString(); + } + public /*this makes no sense*/ void appendTo(MoneyBag m) { + m.appendMoney(this); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/money/MoneyBag.java b/junit4/src/test/java/junit/samples/money/MoneyBag.java new file mode 100644 index 0000000..edc73b5 --- /dev/null +++ b/junit4/src/test/java/junit/samples/money/MoneyBag.java @@ -0,0 +1,124 @@ +package junit.samples.money; + +import java.util.ArrayList; +import java.util.List; + +/** + * A MoneyBag defers exchange rate conversions. For example adding + * 12 Swiss Francs to 14 US Dollars is represented as a bag + * containing the two Monies 12 CHF and 14 USD. Adding another + * 10 Swiss francs gives a bag with 22 CHF and 14 USD. Due to + * the deferred exchange rate conversion we can later value a + * MoneyBag with different exchange rates. + * + * A MoneyBag is represented as a list of Monies and provides + * different constructors to create a MoneyBag. + */ +public class MoneyBag implements IMoney { + private List<Money> fMonies= new ArrayList<Money>(5); + + public static IMoney create(IMoney m1, IMoney m2) { + MoneyBag result= new MoneyBag(); + m1.appendTo(result); + m2.appendTo(result); + return result.simplify(); + } + public IMoney add(IMoney m) { + return m.addMoneyBag(this); + } + public IMoney addMoney(Money m) { + return MoneyBag.create(m, this); + } + public IMoney addMoneyBag(MoneyBag s) { + return MoneyBag.create(s, this); + } + void appendBag(MoneyBag aBag) { + for (Money each : aBag.fMonies) + appendMoney(each); + } + void appendMoney(Money aMoney) { + if (aMoney.isZero()) return; + IMoney old= findMoney(aMoney.currency()); + if (old == null) { + fMonies.add(aMoney); + return; + } + fMonies.remove(old); + Money sum= (Money) old.add(aMoney); + if (sum.isZero()) + return; + fMonies.add(sum); + } + @Override + public boolean equals(Object anObject) { + if (isZero()) + if (anObject instanceof IMoney) + return ((IMoney)anObject).isZero(); + + if (anObject instanceof MoneyBag) { + MoneyBag aMoneyBag= (MoneyBag)anObject; + if (aMoneyBag.fMonies.size() != fMonies.size()) + return false; + + for (Money each : fMonies) + if (! aMoneyBag.contains(each)) + return false; + return true; + } + return false; + } + private Money findMoney(String currency) { + for (Money each : fMonies) + if (each.currency().equals(currency)) + return each; + return null; + } + private boolean contains(Money m) { + Money found= findMoney(m.currency()); + if (found == null) return false; + return found.amount() == m.amount(); + } + @Override + public int hashCode() { + int hash= 0; + for (Money each : fMonies) + hash^= each.hashCode(); + return hash; + } + public boolean isZero() { + return fMonies.size() == 0; + } + public IMoney multiply(int factor) { + MoneyBag result= new MoneyBag(); + if (factor != 0) + for (Money each : fMonies) + result.appendMoney((Money) each.multiply(factor)); + return result; + } + public IMoney negate() { + MoneyBag result= new MoneyBag(); + for (Money each : fMonies) + result.appendMoney((Money) each.negate()); + return result; + } + private IMoney simplify() { + if (fMonies.size() == 1) + return fMonies.iterator().next(); + return this; + } + public IMoney subtract(IMoney m) { + return add(m.negate()); + } + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("{"); + for (Money each : fMonies) + buffer.append(each); + buffer.append("}"); + return buffer.toString(); + } + public void appendTo(MoneyBag m) { + m.appendBag(this); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/money/MoneyTest.java b/junit4/src/test/java/junit/samples/money/MoneyTest.java new file mode 100644 index 0000000..043798d --- /dev/null +++ b/junit4/src/test/java/junit/samples/money/MoneyTest.java @@ -0,0 +1,143 @@ +package junit.samples.money; + +import junit.framework.TestCase; + +public class MoneyTest extends TestCase { + private Money f12CHF; + private Money f14CHF; + private Money f7USD; + private Money f21USD; + + private IMoney fMB1; + private IMoney fMB2; + + public static void main(String args[]) { + junit.textui.TestRunner.run(MoneyTest.class); + } + @Override + protected void setUp() { + f12CHF= new Money(12, "CHF"); + f14CHF= new Money(14, "CHF"); + f7USD= new Money( 7, "USD"); + f21USD= new Money(21, "USD"); + + fMB1= MoneyBag.create(f12CHF, f7USD); + fMB2= MoneyBag.create(f14CHF, f21USD); + } + public void testBagMultiply() { + // {[12 CHF][7 USD]} *2 == {[24 CHF][14 USD]} + IMoney expected= MoneyBag.create(new Money(24, "CHF"), new Money(14, "USD")); + assertEquals(expected, fMB1.multiply(2)); + assertEquals(fMB1, fMB1.multiply(1)); + assertTrue(fMB1.multiply(0).isZero()); + } + public void testBagNegate() { + // {[12 CHF][7 USD]} negate == {[-12 CHF][-7 USD]} + IMoney expected= MoneyBag.create(new Money(-12, "CHF"), new Money(-7, "USD")); + assertEquals(expected, fMB1.negate()); + } + public void testBagSimpleAdd() { + // {[12 CHF][7 USD]} + [14 CHF] == {[26 CHF][7 USD]} + IMoney expected= MoneyBag.create(new Money(26, "CHF"), new Money(7, "USD")); + assertEquals(expected, fMB1.add(f14CHF)); + } + public void testBagSubtract() { + // {[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]} + IMoney expected= MoneyBag.create(new Money(-2, "CHF"), new Money(-14, "USD")); + assertEquals(expected, fMB1.subtract(fMB2)); + } + public void testBagSumAdd() { + // {[12 CHF][7 USD]} + {[14 CHF][21 USD]} == {[26 CHF][28 USD]} + IMoney expected= MoneyBag.create(new Money(26, "CHF"), new Money(28, "USD")); + assertEquals(expected, fMB1.add(fMB2)); + } + public void testIsZero() { + assertTrue(fMB1.subtract(fMB1).isZero()); + assertTrue(MoneyBag.create(new Money (0, "CHF"), new Money (0, "USD")).isZero()); + } + public void testMixedSimpleAdd() { + // [12 CHF] + [7 USD] == {[12 CHF][7 USD]} + IMoney expected= MoneyBag.create(f12CHF, f7USD); + assertEquals(expected, f12CHF.add(f7USD)); + } + public void testBagNotEquals() { + IMoney bag= MoneyBag.create(f12CHF, f7USD); + assertFalse(bag.equals(new Money(12, "DEM").add(f7USD))); + } + public void testMoneyBagEquals() { + assertTrue(!fMB1.equals(null)); + + assertEquals(fMB1, fMB1); + IMoney equal= MoneyBag.create(new Money(12, "CHF"), new Money(7, "USD")); + assertTrue(fMB1.equals(equal)); + assertTrue(!fMB1.equals(f12CHF)); + assertTrue(!f12CHF.equals(fMB1)); + assertTrue(!fMB1.equals(fMB2)); + } + public void testMoneyBagHash() { + IMoney equal= MoneyBag.create(new Money(12, "CHF"), new Money(7, "USD")); + assertEquals(fMB1.hashCode(), equal.hashCode()); + } + public void testMoneyEquals() { + assertTrue(!f12CHF.equals(null)); + Money equalMoney= new Money(12, "CHF"); + assertEquals(f12CHF, f12CHF); + assertEquals(f12CHF, equalMoney); + assertEquals(f12CHF.hashCode(), equalMoney.hashCode()); + assertTrue(!f12CHF.equals(f14CHF)); + } + public void testMoneyHash() { + assertTrue(!f12CHF.equals(null)); + Money equal= new Money(12, "CHF"); + assertEquals(f12CHF.hashCode(), equal.hashCode()); + } + public void testSimplify() { + IMoney money= MoneyBag.create(new Money(26, "CHF"), new Money(28, "CHF")); + assertEquals(new Money(54, "CHF"), money); + } + public void testNormalize2() { + // {[12 CHF][7 USD]} - [12 CHF] == [7 USD] + Money expected= new Money(7, "USD"); + assertEquals(expected, fMB1.subtract(f12CHF)); + } + public void testNormalize3() { + // {[12 CHF][7 USD]} - {[12 CHF][3 USD]} == [4 USD] + IMoney ms1= MoneyBag.create(new Money(12, "CHF"), new Money(3, "USD")); + Money expected= new Money(4, "USD"); + assertEquals(expected, fMB1.subtract(ms1)); + } + public void testNormalize4() { + // [12 CHF] - {[12 CHF][3 USD]} == [-3 USD] + IMoney ms1= MoneyBag.create(new Money(12, "CHF"), new Money(3, "USD")); + Money expected= new Money(-3, "USD"); + assertEquals(expected, f12CHF.subtract(ms1)); + } + public void testPrint() { + assertEquals("[12 CHF]", f12CHF.toString()); + } + public void testSimpleAdd() { + // [12 CHF] + [14 CHF] == [26 CHF] + Money expected= new Money(26, "CHF"); + assertEquals(expected, f12CHF.add(f14CHF)); + } + public void testSimpleBagAdd() { + // [14 CHF] + {[12 CHF][7 USD]} == {[26 CHF][7 USD]} + IMoney expected= MoneyBag.create(new Money(26, "CHF"), new Money(7, "USD")); + assertEquals(expected, f14CHF.add(fMB1)); + } + public void testSimpleMultiply() { + // [14 CHF] *2 == [28 CHF] + Money expected= new Money(28, "CHF"); + assertEquals(expected, f14CHF.multiply(2)); + } + public void testSimpleNegate() { + // [14 CHF] negate == [-14 CHF] + Money expected= new Money(-14, "CHF"); + assertEquals(expected, f14CHF.negate()); + } + public void testSimpleSubtract() { + // [14 CHF] - [12 CHF] == [2 CHF] + Money expected= new Money(2, "CHF"); + assertEquals(expected, f14CHF.subtract(f12CHF)); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/money/package-info.java b/junit4/src/test/java/junit/samples/money/package-info.java new file mode 100644 index 0000000..eb37dd8 --- /dev/null +++ b/junit4/src/test/java/junit/samples/money/package-info.java @@ -0,0 +1,4 @@ +/** + * Example "Money" for JUnit v3.x. + */ +package junit.samples.money;
\ No newline at end of file diff --git a/junit4/src/test/java/junit/samples/package-info.java b/junit4/src/test/java/junit/samples/package-info.java new file mode 100644 index 0000000..7b9ea8f --- /dev/null +++ b/junit4/src/test/java/junit/samples/package-info.java @@ -0,0 +1,4 @@ +/** + * JUnit v3.x examples. + */ +package junit.samples;
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/AllTests.java b/junit4/src/test/java/junit/tests/AllTests.java new file mode 100644 index 0000000..00bbe56 --- /dev/null +++ b/junit4/src/test/java/junit/tests/AllTests.java @@ -0,0 +1,23 @@ +package junit.tests; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * TestSuite that runs all the JUnit tests + * + */ +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() { + TestSuite suite= new TestSuite("Framework Tests"); + suite.addTest(junit.tests.framework.AllTests.suite()); + suite.addTest(junit.tests.runner.AllTests.suite()); + suite.addTest(junit.tests.extensions.AllTests.suite()); + return suite; + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/WasRun.java b/junit4/src/test/java/junit/tests/WasRun.java new file mode 100644 index 0000000..98b83bb --- /dev/null +++ b/junit4/src/test/java/junit/tests/WasRun.java @@ -0,0 +1,15 @@ +package junit.tests; + +import junit.framework.TestCase; + +/** + * A helper test case for testing whether the testing method + * is run. + */ +public class WasRun extends TestCase { + public boolean fWasRun= false; + @Override + protected void runTest() { + fWasRun= true; + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/extensions/ActiveTestTest.java b/junit4/src/test/java/junit/tests/extensions/ActiveTestTest.java new file mode 100644 index 0000000..48987dd --- /dev/null +++ b/junit4/src/test/java/junit/tests/extensions/ActiveTestTest.java @@ -0,0 +1,64 @@ +package junit.tests.extensions; + +import junit.extensions.ActiveTestSuite; +import junit.extensions.RepeatedTest; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; + +/** + * Testing the ActiveTest support + */ + +public class ActiveTestTest extends TestCase { + + public static class SuccessTest extends TestCase { + @Override + public void runTest() { + } + } + + public void testActiveTest() { + Test test= createActiveTestSuite(); + TestResult result= new TestResult(); + test.run(result); + assertEquals(100, result.runCount()); + assertEquals(0, result.failureCount()); + assertEquals(0, result.errorCount()); + } + + public void testActiveRepeatedTest() { + Test test= new RepeatedTest(createActiveTestSuite(), 5); + TestResult result= new TestResult(); + test.run(result); + assertEquals(500, result.runCount()); + assertEquals(0, result.failureCount()); + assertEquals(0, result.errorCount()); + } + + public void testActiveRepeatedTest0() { + Test test= new RepeatedTest(createActiveTestSuite(), 0); + TestResult result= new TestResult(); + test.run(result); + assertEquals(0, result.runCount()); + assertEquals(0, result.failureCount()); + assertEquals(0, result.errorCount()); + } + + public void testActiveRepeatedTest1() { + Test test= new RepeatedTest(createActiveTestSuite(), 1); + TestResult result= new TestResult(); + test.run(result); + assertEquals(100, result.runCount()); + assertEquals(0, result.failureCount()); + assertEquals(0, result.errorCount()); + } + + ActiveTestSuite createActiveTestSuite() { + ActiveTestSuite suite= new ActiveTestSuite(); + for (int i= 0; i < 100; i++) + suite.addTest(new SuccessTest()); + return suite; + } + +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/extensions/AllTests.java b/junit4/src/test/java/junit/tests/extensions/AllTests.java new file mode 100644 index 0000000..92de10b --- /dev/null +++ b/junit4/src/test/java/junit/tests/extensions/AllTests.java @@ -0,0 +1,23 @@ +package junit.tests.extensions; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * TestSuite that runs all the extension tests + * + */ +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() { // Collect tests manually because we have to test class collection code + TestSuite suite= new TestSuite("Framework Tests"); + suite.addTestSuite(ExtensionTest.class); + suite.addTestSuite(ActiveTestTest.class); + suite.addTestSuite(RepeatedTestTest.class); + return suite; + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/extensions/ExtensionTest.java b/junit4/src/test/java/junit/tests/extensions/ExtensionTest.java new file mode 100644 index 0000000..d8a5c09 --- /dev/null +++ b/junit4/src/test/java/junit/tests/extensions/ExtensionTest.java @@ -0,0 +1,98 @@ +package junit.tests.extensions; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import junit.tests.WasRun; + +/** + * A test case testing the extensions to the testing framework. + * + */ +public class ExtensionTest extends TestCase { + static class TornDown extends TestSetup { + boolean fTornDown= false; + + TornDown(Test test) { + super(test); + } + @Override + protected void tearDown() { + fTornDown= true; + } + } + public void testRunningErrorInTestSetup() { + TestCase test= new TestCase("failure") { + @Override + public void runTest() { + fail(); + } + }; + + TestSetup wrapper= new TestSetup(test); + + TestResult result= new TestResult(); + wrapper.run(result); + assertTrue(!result.wasSuccessful()); + } + public void testRunningErrorsInTestSetup() { + TestCase failure= new TestCase("failure") { + @Override + public void runTest() { + fail(); + } + }; + + TestCase error= new TestCase("error") { + @Override + public void runTest() { + throw new Error(); + } + }; + + TestSuite suite= new TestSuite(); + suite.addTest(failure); + suite.addTest(error); + + TestSetup wrapper= new TestSetup(suite); + + TestResult result= new TestResult(); + wrapper.run(result); + + assertEquals(1, result.failureCount()); + assertEquals(1, result.errorCount()); + } + public void testSetupErrorDontTearDown() { + WasRun test= new WasRun(); + + TornDown wrapper= new TornDown(test) { + @Override + public void setUp() { + fail(); + } + }; + + TestResult result= new TestResult(); + wrapper.run(result); + + assertTrue(!wrapper.fTornDown); + } + public void testSetupErrorInTestSetup() { + WasRun test= new WasRun(); + + TestSetup wrapper= new TestSetup(test) { + @Override + public void setUp() { + fail(); + } + }; + + TestResult result= new TestResult(); + wrapper.run(result); + + assertTrue(!test.fWasRun); + assertTrue(!result.wasSuccessful()); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/extensions/RepeatedTestTest.java b/junit4/src/test/java/junit/tests/extensions/RepeatedTestTest.java new file mode 100644 index 0000000..9e53bfa --- /dev/null +++ b/junit4/src/test/java/junit/tests/extensions/RepeatedTestTest.java @@ -0,0 +1,63 @@ +package junit.tests.extensions; + +import junit.extensions.RepeatedTest; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +/** + * Testing the RepeatedTest support. + */ + +public class RepeatedTestTest extends TestCase { + private TestSuite fSuite; + + public static class SuccessTest extends TestCase { + + @Override + public void runTest() { + } + } + + public RepeatedTestTest(String name) { + super(name); + fSuite= new TestSuite(); + fSuite.addTest(new SuccessTest()); + fSuite.addTest(new SuccessTest()); + } + + public void testRepeatedOnce() { + Test test= new RepeatedTest(fSuite, 1); + assertEquals(2, test.countTestCases()); + TestResult result= new TestResult(); + test.run(result); + assertEquals(2, result.runCount()); + } + + public void testRepeatedMoreThanOnce() { + Test test= new RepeatedTest(fSuite, 3); + assertEquals(6, test.countTestCases()); + TestResult result= new TestResult(); + test.run(result); + assertEquals(6, result.runCount()); + } + + public void testRepeatedZero() { + Test test= new RepeatedTest(fSuite, 0); + assertEquals(0, test.countTestCases()); + TestResult result= new TestResult(); + test.run(result); + assertEquals(0, result.runCount()); + } + + public void testRepeatedNegative() { + try { + new RepeatedTest(fSuite, -1); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains(">=")); + return; + } + fail("Should throw an IllegalArgumentException"); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/extensions/package-info.java b/junit4/src/test/java/junit/tests/extensions/package-info.java new file mode 100644 index 0000000..acc0194 --- /dev/null +++ b/junit4/src/test/java/junit/tests/extensions/package-info.java @@ -0,0 +1,4 @@ +/** + * Tests for the JUnit v3.x extension functionality. + */ +package junit.tests.extensions;
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/AllTests.java b/junit4/src/test/java/junit/tests/framework/AllTests.java new file mode 100644 index 0000000..6ec9891 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/AllTests.java @@ -0,0 +1,32 @@ +package junit.tests.framework; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * TestSuite that runs all the sample tests + * + */ +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() { + TestSuite suite= new TestSuite("Framework Tests"); + suite.addTestSuite(TestCaseTest.class); + suite.addTest(SuiteTest.suite()); // Tests suite building, so can't use automatic test extraction + suite.addTestSuite(TestListenerTest.class); + suite.addTestSuite(AssertionFailedErrorTest.class); + suite.addTestSuite(AssertTest.class); + suite.addTestSuite(TestImplementorTest.class); + suite.addTestSuite(NoArgTestCaseTest.class); + suite.addTestSuite(ComparisonCompactorTest.class); + suite.addTestSuite(ComparisonFailureTest.class); + suite.addTestSuite(DoublePrecisionAssertTest.class); + suite.addTestSuite(FloatAssertTest.class); + return suite; + } + +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/AssertTest.java b/junit4/src/test/java/junit/tests/framework/AssertTest.java new file mode 100644 index 0000000..de33ff6 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/AssertTest.java @@ -0,0 +1,171 @@ +package junit.tests.framework; + +import junit.framework.AssertionFailedError; +import junit.framework.ComparisonFailure; +import junit.framework.TestCase; + +public class AssertTest extends TestCase { + + /* In the tests that follow, we can't use standard formatting + * for exception tests: + * try { + * somethingThatShouldThrow(); + * fail(); + * catch (AssertionFailedError e) { + * } + * because fail() would never be reported. + */ + public void testFail() { + // Also, we are testing fail, so we can't rely on fail() working. + // We have to throw the exception manually. + try { + fail(); + } catch (AssertionFailedError e) { + return; + } + throw new AssertionFailedError(); + } + + public void testAssertionFailedErrorToStringWithNoMessage() { + // Also, we are testing fail, so we can't rely on fail() working. + // We have to throw the exception manually. + try { + fail(); + } catch (AssertionFailedError e) { + assertEquals("junit.framework.AssertionFailedError", e.toString()); + return; + } + throw new AssertionFailedError(); + } + + public void testAssertionFailedErrorToStringWithMessage() { + // Also, we are testing fail, so we can't rely on fail() working. + // We have to throw the exception manually. + try { + fail("woops!"); + } catch (AssertionFailedError e) { + assertEquals("junit.framework.AssertionFailedError: woops!", e.toString()); + return; + } + throw new AssertionFailedError(); + } + + public void testAssertEquals() { + Object o= new Object(); + assertEquals(o, o); + try { + assertEquals(new Object(), new Object()); + } catch (AssertionFailedError e) { + return; + } + fail(); + } + + public void testAssertEqualsNull() { + assertEquals((Object) null, (Object) null); + } + + public void testAssertStringEquals() { + assertEquals("a", "a"); + } + + public void testAssertNullNotEqualsString() { + try { + assertEquals(null, "foo"); + fail(); + } catch (ComparisonFailure e) { + } + } + + public void testAssertStringNotEqualsNull() { + try { + assertEquals("foo", null); + fail(); + } catch (ComparisonFailure e) { + e.getMessage(); // why no assertion? + } + } + + public void testAssertNullNotEqualsNull() { + try { + assertEquals(null, new Object()); + } catch (AssertionFailedError e) { + e.getMessage(); // why no assertion? + return; + } + fail(); + } + + public void testAssertNull() { + assertNull(null); + try { + assertNull(new Object()); + } catch (AssertionFailedError e) { + return; + } + fail(); + } + + public void testAssertNotNull() { + assertNotNull(new Object()); + try { + assertNotNull(null); + } catch (AssertionFailedError e) { + return; + } + fail(); + } + + public void testAssertTrue() { + assertTrue(true); + try { + assertTrue(false); + } catch (AssertionFailedError e) { + return; + } + fail(); + } + + public void testAssertFalse() { + assertFalse(false); + try { + assertFalse(true); + } catch (AssertionFailedError e) { + return; + } + fail(); + } + + public void testAssertSame() { + Object o= new Object(); + assertSame(o, o); + try { + assertSame(new Integer(1), new Integer(1)); + } catch (AssertionFailedError e) { + return; + } + fail(); + } + + public void testAssertNotSame() { + assertNotSame(new Integer(1), null); + assertNotSame(null, new Integer(1)); + assertNotSame(new Integer(1), new Integer(1)); + try { + Integer obj= new Integer(1); + assertNotSame(obj, obj); + } catch (AssertionFailedError e) { + return; + } + fail(); + } + + public void testAssertNotSameFailsNull() { + try { + assertNotSame(null, null); + } catch (AssertionFailedError e) { + return; + } + fail(); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/AssertionFailedErrorTest.java b/junit4/src/test/java/junit/tests/framework/AssertionFailedErrorTest.java new file mode 100644 index 0000000..3dd6b1f --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/AssertionFailedErrorTest.java @@ -0,0 +1,23 @@ +package junit.tests.framework; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +public class AssertionFailedErrorTest extends TestCase { + private static final String ARBITRARY_MESSAGE= "arbitrary message"; + + public void testCreateErrorWithoutMessage() throws Exception { + AssertionFailedError error= new AssertionFailedError(); + assertNull(error.getMessage()); + } + + public void testCreateErrorWithMessage() throws Exception { + AssertionFailedError error= new AssertionFailedError(ARBITRARY_MESSAGE); + assertEquals(ARBITRARY_MESSAGE, error.getMessage()); + } + + public void testCreateErrorWithoutMessageInsteadOfNull() throws Exception { + AssertionFailedError error= new AssertionFailedError(null); + assertEquals("", error.getMessage()); + } +} diff --git a/junit4/src/test/java/junit/tests/framework/ComparisonCompactorTest.java b/junit4/src/test/java/junit/tests/framework/ComparisonCompactorTest.java new file mode 100644 index 0000000..6edaefe --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/ComparisonCompactorTest.java @@ -0,0 +1,102 @@ +package junit.tests.framework; + +import junit.framework.ComparisonCompactor; +import junit.framework.TestCase; + +public class ComparisonCompactorTest extends TestCase { + + public void testMessage() { + String failure= new ComparisonCompactor(0, "b", "c").compact("a"); + assertTrue("a expected:<[b]> but was:<[c]>".equals(failure)); + } + + public void testStartSame() { + String failure= new ComparisonCompactor(1, "ba", "bc").compact(null); + assertEquals("expected:<b[a]> but was:<b[c]>", failure); + } + + public void testEndSame() { + String failure= new ComparisonCompactor(1, "ab", "cb").compact(null); + assertEquals("expected:<[a]b> but was:<[c]b>", failure); + } + + public void testSame() { + String failure= new ComparisonCompactor(1, "ab", "ab").compact(null); + assertEquals("expected:<ab> but was:<ab>", failure); + } + + public void testNoContextStartAndEndSame() { + String failure= new ComparisonCompactor(0, "abc", "adc").compact(null); + assertEquals("expected:<...[b]...> but was:<...[d]...>", failure); + } + + public void testStartAndEndContext() { + String failure= new ComparisonCompactor(1, "abc", "adc").compact(null); + assertEquals("expected:<a[b]c> but was:<a[d]c>", failure); + } + + public void testStartAndEndContextWithEllipses() { + String failure= new ComparisonCompactor(1, "abcde", "abfde").compact(null); + assertEquals("expected:<...b[c]d...> but was:<...b[f]d...>", failure); + } + + public void testComparisonErrorStartSameComplete() { + String failure= new ComparisonCompactor(2, "ab", "abc").compact(null); + assertEquals("expected:<ab[]> but was:<ab[c]>", failure); + } + + public void testComparisonErrorEndSameComplete() { + String failure= new ComparisonCompactor(0, "bc", "abc").compact(null); + assertEquals("expected:<[]...> but was:<[a]...>", failure); + } + + public void testComparisonErrorEndSameCompleteContext() { + String failure= new ComparisonCompactor(2, "bc", "abc").compact(null); + assertEquals("expected:<[]bc> but was:<[a]bc>", failure); + } + + public void testComparisonErrorOverlapingMatches() { + String failure= new ComparisonCompactor(0, "abc", "abbc").compact(null); + assertEquals("expected:<...[]...> but was:<...[b]...>", failure); + } + + public void testComparisonErrorOverlapingMatchesContext() { + String failure= new ComparisonCompactor(2, "abc", "abbc").compact(null); + assertEquals("expected:<ab[]c> but was:<ab[b]c>", failure); + } + + public void testComparisonErrorOverlapingMatches2() { + String failure= new ComparisonCompactor(0, "abcdde", "abcde").compact(null); + assertEquals("expected:<...[d]...> but was:<...[]...>", failure); + } + + public void testComparisonErrorOverlapingMatches2Context() { + String failure= new ComparisonCompactor(2, "abcdde", "abcde").compact(null); + assertEquals("expected:<...cd[d]e> but was:<...cd[]e>", failure); + } + + public void testComparisonErrorWithActualNull() { + String failure= new ComparisonCompactor(0, "a", null).compact(null); + assertEquals("expected:<a> but was:<null>", failure); + } + + public void testComparisonErrorWithActualNullContext() { + String failure= new ComparisonCompactor(2, "a", null).compact(null); + assertEquals("expected:<a> but was:<null>", failure); + } + + public void testComparisonErrorWithExpectedNull() { + String failure= new ComparisonCompactor(0, null, "a").compact(null); + assertEquals("expected:<null> but was:<a>", failure); + } + + public void testComparisonErrorWithExpectedNullContext() { + String failure= new ComparisonCompactor(2, null, "a").compact(null); + assertEquals("expected:<null> but was:<a>", failure); + } + + public void testBug609972() { + String failure= new ComparisonCompactor(10, "S&P500", "0").compact(null); + assertEquals("expected:<[S&P50]0> but was:<[]0>", failure); + } +} diff --git a/junit4/src/test/java/junit/tests/framework/ComparisonFailureTest.java b/junit4/src/test/java/junit/tests/framework/ComparisonFailureTest.java new file mode 100644 index 0000000..8a1e5f2 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/ComparisonFailureTest.java @@ -0,0 +1,47 @@ +package junit.tests.framework; + +import junit.framework.ComparisonFailure; +import junit.framework.TestCase; + +public class ComparisonFailureTest extends TestCase { + + // Most of the tests are in ComparisonCompactorTest + public void testConnection() { + ComparisonFailure failure= new ComparisonFailure("warning", "Mary had a little lamb", "Mary had the little lamb"); + assertEquals("warning expected:<Mary had [a] little lamb> but was:<Mary had [the] little lamb>", failure.getMessage()); + } + + // This is like an instanceof test. + public void testThrowing() { + try { + assertEquals("a", "b"); + } catch (ComparisonFailure e) { + return; + } + fail(); + } + + public void testExceptionToStringWithMessage() { + try { + assertEquals("woops!", "a", "b"); + } catch (ComparisonFailure e) { + if (!e.toString().startsWith("junit.framework.ComparisonFailure: woops! expected:<")) { + fail("Unexpected message: " + e); + } + return; + } + fail(); + } + + public void testExceptionToStringWithoutMessage() { + try { + assertEquals("a", "b"); + } catch (ComparisonFailure e) { + if (!e.toString().startsWith("junit.framework.ComparisonFailure: expected:<")) { + fail("Unexpected message: " + e); + } + return; + } + fail(); + } +} diff --git a/junit4/src/test/java/junit/tests/framework/DoublePrecisionAssertTest.java b/junit4/src/test/java/junit/tests/framework/DoublePrecisionAssertTest.java new file mode 100644 index 0000000..9df3560 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/DoublePrecisionAssertTest.java @@ -0,0 +1,55 @@ +package junit.tests.framework; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +public class DoublePrecisionAssertTest extends TestCase { + + /** + * Test for the special Double.NaN value. + */ + public void testAssertEqualsNaNFails() { + try { + assertEquals(1.234, Double.NaN, 0.0); + fail(); + } catch (AssertionFailedError e) { + } + } + + public void testAssertNaNEqualsFails() { + try { + assertEquals(Double.NaN, 1.234, 0.0); + fail(); + } catch (AssertionFailedError e) { + } + } + + public void testAssertNaNEqualsNaN() { + assertEquals(Double.NaN, Double.NaN, 0.0); + } + + public void testAssertPosInfinityNotEqualsNegInfinity() { + try { + assertEquals(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 0.0); + fail(); + } catch (AssertionFailedError e) { + } + } + + public void testAssertPosInfinityNotEquals() { + try { + assertEquals(Double.POSITIVE_INFINITY, 1.23, 0.0); + fail(); + } catch (AssertionFailedError e) { + } + } + + public void testAssertPosInfinityEqualsInfinity() { + assertEquals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0.0); + } + + public void testAssertNegInfinityEqualsInfinity() { + assertEquals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0.0); + } + +} diff --git a/junit4/src/test/java/junit/tests/framework/Failure.java b/junit4/src/test/java/junit/tests/framework/Failure.java new file mode 100644 index 0000000..4dd9c8c --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/Failure.java @@ -0,0 +1,14 @@ +package junit.tests.framework; + +import junit.framework.TestCase; + +/** + * A test case testing the testing framework. + * + */ +public class Failure extends TestCase { + @Override + public void runTest() { + fail(); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/FloatAssertTest.java b/junit4/src/test/java/junit/tests/framework/FloatAssertTest.java new file mode 100644 index 0000000..2d22549 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/FloatAssertTest.java @@ -0,0 +1,63 @@ +package junit.tests.framework; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +public class FloatAssertTest extends TestCase { + + /** + * Test for the special Double.NaN value. + */ + public void testAssertEqualsNaNFails() { + try { + assertEquals(1.234f, Float.NaN, 0.0); + fail(); + } catch (AssertionFailedError e) { + } + } + + public void testAssertNaNEqualsFails() { + try { + assertEquals(Float.NaN, 1.234f, 0.0); + fail(); + } catch (AssertionFailedError e) { + } + } + + public void testAssertNaNEqualsNaN() { + assertEquals(Float.NaN, Float.NaN, 0.0); + } + + public void testAssertPosInfinityNotEqualsNegInfinity() { + try { + assertEquals(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, 0.0); + fail(); + } catch (AssertionFailedError e) { + } + } + + public void testAssertPosInfinityNotEquals() { + try { + assertEquals(Float.POSITIVE_INFINITY, 1.23f, 0.0); + fail(); + } catch (AssertionFailedError e) { + } + } + + public void testAssertPosInfinityEqualsInfinity() { + assertEquals(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, 0.0); + } + + public void testAssertNegInfinityEqualsInfinity() { + assertEquals(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, 0.0); + } + + public void testAllInfinities() { + try { + assertEquals(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY); + fail(); + } catch (AssertionFailedError e) { + } + } + +} diff --git a/junit4/src/test/java/junit/tests/framework/InheritedTestCase.java b/junit4/src/test/java/junit/tests/framework/InheritedTestCase.java new file mode 100644 index 0000000..f272d77 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/InheritedTestCase.java @@ -0,0 +1,9 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ +public class InheritedTestCase extends OneTestCase { + public void test2() { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/NoArgTestCaseTest.java b/junit4/src/test/java/junit/tests/framework/NoArgTestCaseTest.java new file mode 100644 index 0000000..70281e6 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/NoArgTestCaseTest.java @@ -0,0 +1,9 @@ + +package junit.tests.framework; + +import junit.framework.TestCase; + +public class NoArgTestCaseTest extends TestCase { + public void testNothing() { // If this compiles, the no arg ctor is there + } +} diff --git a/junit4/src/test/java/junit/tests/framework/NoTestCaseClass.java b/junit4/src/test/java/junit/tests/framework/NoTestCaseClass.java new file mode 100644 index 0000000..5fea04d --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/NoTestCaseClass.java @@ -0,0 +1,10 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ + +public class NoTestCaseClass extends Object { + public void testSuccess() { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/NoTestCases.java b/junit4/src/test/java/junit/tests/framework/NoTestCases.java new file mode 100644 index 0000000..fec60d0 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/NoTestCases.java @@ -0,0 +1,11 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ +import junit.framework.TestCase; + +public class NoTestCases extends TestCase { + public void noTestCase() { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/NotPublicTestCase.java b/junit4/src/test/java/junit/tests/framework/NotPublicTestCase.java new file mode 100644 index 0000000..085f985 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/NotPublicTestCase.java @@ -0,0 +1,13 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ +import junit.framework.TestCase; + +public class NotPublicTestCase extends TestCase { + protected void testNotPublic() { + } + public void testPublic() { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/NotVoidTestCase.java b/junit4/src/test/java/junit/tests/framework/NotVoidTestCase.java new file mode 100644 index 0000000..eca1a63 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/NotVoidTestCase.java @@ -0,0 +1,14 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ +import junit.framework.TestCase; + +public class NotVoidTestCase extends TestCase { + public int testNotVoid() { + return 1; + } + public void testVoid() { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/OneTestCase.java b/junit4/src/test/java/junit/tests/framework/OneTestCase.java new file mode 100644 index 0000000..b4ca560 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/OneTestCase.java @@ -0,0 +1,15 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ +import junit.framework.TestCase; + +public class OneTestCase extends TestCase { + public void noTestCase() { + } + public void testCase() { + } + public void testCase(int arg) { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/OverrideTestCase.java b/junit4/src/test/java/junit/tests/framework/OverrideTestCase.java new file mode 100644 index 0000000..043ef81 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/OverrideTestCase.java @@ -0,0 +1,10 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ +public class OverrideTestCase extends OneTestCase { + @Override + public void testCase() { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/Success.java b/junit4/src/test/java/junit/tests/framework/Success.java new file mode 100644 index 0000000..ed4d1b6 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/Success.java @@ -0,0 +1,17 @@ +package junit.tests.framework; + +import junit.framework.TestCase; + +/** + * A test case testing the testing framework. + * + */ +public class Success extends TestCase { + + @Override + public void runTest() { + } + + public void testSuccess() { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/SuiteTest.java b/junit4/src/test/java/junit/tests/framework/SuiteTest.java new file mode 100644 index 0000000..3953f4f --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/SuiteTest.java @@ -0,0 +1,105 @@ +package junit.tests.framework; + +import java.util.Collections; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +/** + * A fixture for testing the "auto" test suite feature. + * + */ +public class SuiteTest extends TestCase { + protected TestResult fResult; + public SuiteTest(String name) { + super(name); + } + @Override + protected void setUp() { + fResult= new TestResult(); + } + public static Test suite() { + TestSuite suite= new TestSuite("Suite Tests"); + // build the suite manually, because some of the suites are testing + // the functionality that automatically builds suites + suite.addTest(new SuiteTest("testNoTestCases")); + suite.addTest(new SuiteTest("testOneTestCase")); + suite.addTest(new SuiteTest("testNotPublicTestCase")); + suite.addTest(new SuiteTest("testNotVoidTestCase")); + suite.addTest(new SuiteTest("testNotExistingTestCase")); + suite.addTest(new SuiteTest("testInheritedTests")); + suite.addTest(new SuiteTest("testOneTestCaseEclipseSeesSameStructureAs381")); + suite.addTest(new SuiteTest("testNoTestCaseClass")); + suite.addTest(new SuiteTest("testShadowedTests")); + suite.addTest(new SuiteTest("testAddTestSuite")); + suite.addTest(new SuiteTest("testCreateSuiteFromArray")); + + return suite; + } + public void testInheritedTests() { + TestSuite suite= new TestSuite(InheritedTestCase.class); + suite.run(fResult); + assertTrue(fResult.wasSuccessful()); + assertEquals(2, fResult.runCount()); + } + public void testNoTestCaseClass() { + Test t= new TestSuite(NoTestCaseClass.class); + t.run(fResult); + assertEquals(1, fResult.runCount()); // warning test + assertTrue(! fResult.wasSuccessful()); + } + public void testNoTestCases() { + Test t= new TestSuite(NoTestCases.class); + t.run(fResult); + assertTrue(fResult.runCount() == 1); // warning test + assertTrue(fResult.failureCount() == 1); + assertTrue(! fResult.wasSuccessful()); + } + public void testNotExistingTestCase() { + Test t= new SuiteTest("notExistingMethod"); + t.run(fResult); + assertTrue(fResult.runCount() == 1); + assertTrue(fResult.failureCount() == 1); + assertTrue(fResult.errorCount() == 0); + } + public void testNotPublicTestCase() { + TestSuite suite= new TestSuite(NotPublicTestCase.class); + // 1 public test case + 1 warning for the non-public test case + assertEquals(2, suite.countTestCases()); + } + public void testNotVoidTestCase() { + TestSuite suite= new TestSuite(NotVoidTestCase.class); + assertTrue(suite.countTestCases() == 1); + } + public void testOneTestCase() { + TestSuite t= new TestSuite(OneTestCase.class); + t.run(fResult); + assertTrue(fResult.runCount() == 1); + assertTrue(fResult.failureCount() == 0); + assertTrue(fResult.errorCount() == 0); + assertTrue(fResult.wasSuccessful()); + } + public void testOneTestCaseEclipseSeesSameStructureAs381() { + TestSuite t= new TestSuite(ThreeTestCases.class); + assertEquals(3, Collections.list(t.tests()).size()); + } + public void testShadowedTests() { + TestSuite suite= new TestSuite(OverrideTestCase.class); + suite.run(fResult); + assertEquals(1, fResult.runCount()); + } + public void testAddTestSuite() { + TestSuite suite= new TestSuite(); + suite.addTestSuite(OneTestCase.class); + suite.run(fResult); + assertEquals(1, fResult.runCount()); + } + public void testCreateSuiteFromArray() { + TestSuite suite = new TestSuite(OneTestCase.class, DoublePrecisionAssertTest.class); + assertEquals(2, suite.testCount()); + assertEquals("junit.tests.framework.DoublePrecisionAssertTest" , ((TestSuite)suite.testAt(1)).getName()); + assertEquals("junit.tests.framework.OneTestCase" , ((TestSuite)suite.testAt(0)).getName()); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/TestCaseTest.java b/junit4/src/test/java/junit/tests/framework/TestCaseTest.java new file mode 100644 index 0000000..91b91e6 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/TestCaseTest.java @@ -0,0 +1,190 @@ +package junit.tests.framework; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestFailure; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import junit.tests.WasRun; + +/** + * A test case testing the testing framework. + * + */ +public class TestCaseTest extends TestCase { + + static class TornDown extends TestCase { + boolean fTornDown= false; + + @Override + protected void tearDown() { + fTornDown= true; + } + @Override + protected void runTest() { + throw new Error("running"); + } + } + + public void testCaseToString() { + // This test wins the award for twisted snake tail eating while + // writing self tests. And you thought those weird anonymous + // inner classes were bad... + assertEquals("testCaseToString(junit.tests.framework.TestCaseTest)", toString()); + } + public void testError() { + TestCase error= new TestCase("error") { + @Override + protected void runTest() { + throw new Error(); + } + }; + verifyError(error); + } + public void testRunAndTearDownFails() { + TornDown fails= new TornDown() { + @Override + protected void tearDown() { + super.tearDown(); + throw new Error(); + } + @Override + protected void runTest() { + throw new Error(); + } + }; + verifyError(fails); + assertTrue(fails.fTornDown); + } + public void testSetupFails() { + TestCase fails= new TestCase("success") { + @Override + protected void setUp() { + throw new Error(); + } + @Override + protected void runTest() { + } + }; + verifyError(fails); + } + public void testSuccess() { + TestCase success= new TestCase("success") { + @Override + protected void runTest() { + } + }; + verifySuccess(success); + } + public void testFailure() { + TestCase failure= new TestCase("failure") { + @Override + protected void runTest() { + fail(); + } + }; + verifyFailure(failure); + } + + public void testTearDownAfterError() { + TornDown fails= new TornDown(); + verifyError(fails); + assertTrue(fails.fTornDown); + } + + public void testTearDownFails() { + TestCase fails= new TestCase("success") { + @Override + protected void tearDown() { + throw new Error(); + } + @Override + protected void runTest() { + } + }; + verifyError(fails); + } + public void testTearDownSetupFails() { + TornDown fails= new TornDown() { + @Override + protected void setUp() { + throw new Error(); + } + }; + verifyError(fails); + assertTrue(!fails.fTornDown); + } + public void testWasRun() { + WasRun test= new WasRun(); + test.run(); + assertTrue(test.fWasRun); + } + public void testExceptionRunningAndTearDown() { + // With 1.4, we should + // wrap the exception thrown while running with the exception thrown + // while tearing down + Test t= new TornDown() { + @Override + public void tearDown() { + throw new Error("tearingDown"); + } + }; + TestResult result= new TestResult(); + t.run(result); + TestFailure failure= result.errors().nextElement(); + assertEquals("running", failure.thrownException().getMessage()); + } + + public void testErrorTearingDownDoesntMaskErrorRunning() { + final Exception running= new Exception("Running"); + TestCase t= new TestCase() { + @Override + protected void runTest() throws Throwable { + throw running; + } + @Override + protected void tearDown() throws Exception { + throw new Error("Tearing down"); + } + }; + try { + t.runBare(); + } catch (Throwable thrown) { + assertSame(running, thrown); + } + } + + public void testNoArgTestCasePasses() { + Test t= new TestSuite(NoArgTestCaseTest.class); + TestResult result= new TestResult(); + t.run(result); + assertTrue(result.runCount() == 1); + assertTrue(result.failureCount() == 0); + assertTrue(result.errorCount() == 0); + } + + public void testNamelessTestCase() { + TestCase t= new TestCase() {}; + TestResult result = t.run(); + assertEquals(1, result.failureCount()); + } + + void verifyError(TestCase test) { + TestResult result= test.run(); + assertTrue(result.runCount() == 1); + assertTrue(result.failureCount() == 0); + assertTrue(result.errorCount() == 1); + } + void verifyFailure(TestCase test) { + TestResult result= test.run(); + assertTrue(result.runCount() == 1); + assertTrue(result.failureCount() == 1); + assertTrue(result.errorCount() == 0); + } + void verifySuccess(TestCase test) { + TestResult result= test.run(); + assertTrue(result.runCount() == 1); + assertTrue(result.failureCount() == 0); + assertTrue(result.errorCount() == 0); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/TestImplementorTest.java b/junit4/src/test/java/junit/tests/framework/TestImplementorTest.java new file mode 100644 index 0000000..a5f0962 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/TestImplementorTest.java @@ -0,0 +1,54 @@ +package junit.tests.framework; + +import junit.framework.Protectable; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; + +/** + * Test an implementor of junit.framework.Test other than TestCase or TestSuite + */ +public class TestImplementorTest extends TestCase { + public static class DoubleTestCase implements Test { + private TestCase fTestCase; + + public DoubleTestCase(TestCase testCase) { + fTestCase= testCase; + } + + public int countTestCases() { + return 2; + } + + public void run(TestResult result) { + result.startTest(this); + Protectable p= new Protectable() { + public void protect() throws Throwable { + fTestCase.runBare(); + fTestCase.runBare(); + } + }; + result.runProtected(this, p); + result.endTest(this); + } + } + + private DoubleTestCase fTest; + + public TestImplementorTest() { + TestCase testCase= new TestCase() { + @Override + public void runTest() { + } + }; + fTest= new DoubleTestCase(testCase); + } + + public void testSuccessfulRun() { + TestResult result= new TestResult(); + fTest.run(result); + assertEquals(fTest.countTestCases(), result.runCount()); + assertEquals(0, result.errorCount()); + assertEquals(0, result.failureCount()); + } +} diff --git a/junit4/src/test/java/junit/tests/framework/TestListenerTest.java b/junit4/src/test/java/junit/tests/framework/TestListenerTest.java new file mode 100644 index 0000000..1da2af5 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/TestListenerTest.java @@ -0,0 +1,73 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestListener; +import junit.framework.TestResult; + +public class TestListenerTest extends TestCase implements TestListener { + private TestResult fResult; + private int fStartCount; + private int fEndCount; + private int fFailureCount; + private int fErrorCount; + + public void addError(Test test, Throwable t) { + fErrorCount++; + } + public void addFailure(Test test, AssertionFailedError t) { + fFailureCount++; + } + public void endTest(Test test) { + fEndCount++; + } + @Override + protected void setUp() { + fResult= new TestResult(); + fResult.addListener(this); + + fStartCount= 0; + fEndCount= 0; + fFailureCount= 0; + fErrorCount= 0; + } + public void startTest(Test test) { + fStartCount++; + } + public void testError() { + TestCase test= new TestCase("noop") { + @Override + public void runTest() { + throw new Error(); + } + }; + test.run(fResult); + assertEquals(1, fErrorCount); + assertEquals(1, fEndCount); + } + public void testFailure() { + TestCase test= new TestCase("noop") { + @Override + public void runTest() { + fail(); + } + }; + test.run(fResult); + assertEquals(1, fFailureCount); + assertEquals(1, fEndCount); + } + public void testStartStop() { + TestCase test= new TestCase("noop") { + @Override + public void runTest() { + } + }; + test.run(fResult); + assertEquals(1, fStartCount); + assertEquals(1, fEndCount); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/ThreeTestCases.java b/junit4/src/test/java/junit/tests/framework/ThreeTestCases.java new file mode 100644 index 0000000..c58bece --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/ThreeTestCases.java @@ -0,0 +1,15 @@ +package junit.tests.framework; + +/** + * Test class used in SuiteTest + */ +import junit.framework.TestCase; + +public class ThreeTestCases extends TestCase { + public void testCase() { + } + public void testCase2() { + } + public void testCase3thisTimeItsPersonal() { + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/framework/package-info.java b/junit4/src/test/java/junit/tests/framework/package-info.java new file mode 100644 index 0000000..9cc0024 --- /dev/null +++ b/junit4/src/test/java/junit/tests/framework/package-info.java @@ -0,0 +1,4 @@ +/** + * Tests the JUnit v3.x core classes. + */ +package junit.tests.framework;
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/package-info.java b/junit4/src/test/java/junit/tests/package-info.java new file mode 100644 index 0000000..d23121b --- /dev/null +++ b/junit4/src/test/java/junit/tests/package-info.java @@ -0,0 +1,4 @@ +/** + * Tests the JUnit v3.x framework. + */ +package junit.tests;
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/runner/AllTests.java b/junit4/src/test/java/junit/tests/runner/AllTests.java new file mode 100644 index 0000000..a0aa116 --- /dev/null +++ b/junit4/src/test/java/junit/tests/runner/AllTests.java @@ -0,0 +1,31 @@ +package junit.tests.runner; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * TestSuite that runs all the sample tests + * + */ +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() { // Collect tests manually because we have to test class collection code + TestSuite suite= new TestSuite("Framework Tests"); + suite.addTestSuite(StackFilterTest.class); + suite.addTestSuite(ResultTest.class); + suite.addTestSuite(BaseTestRunnerTest.class); + suite.addTestSuite(TextFeedbackTest.class); + suite.addTestSuite(TextRunnerSingleMethodTest.class); + suite.addTestSuite(TextRunnerTest.class); + return suite; + } + + static boolean isJDK11() { + String version= System.getProperty("java.version"); + return version.startsWith("1.1"); + } +} diff --git a/junit4/src/test/java/junit/tests/runner/BaseTestRunnerTest.java b/junit4/src/test/java/junit/tests/runner/BaseTestRunnerTest.java new file mode 100644 index 0000000..8943da9 --- /dev/null +++ b/junit4/src/test/java/junit/tests/runner/BaseTestRunnerTest.java @@ -0,0 +1,53 @@ + +package junit.tests.runner; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import junit.runner.BaseTestRunner; + +public class BaseTestRunnerTest extends TestCase { + public static class MockRunner extends BaseTestRunner { + private boolean fRunFailed = false; + + @Override + protected void runFailed(String message) { + fRunFailed = true; + } + + @Override + public void testEnded(String testName) { + } + + @Override + public void testFailed(int status, Test test, Throwable t) { + } + + @Override + public void testStarted(String testName) { + } + } + + public static class NonStatic { + public Test suite() { + return null; + } + } + + public void testInvokeNonStaticSuite() { + BaseTestRunner runner= new MockRunner(); + runner.getTest("junit.tests.runner.BaseTestRunnerTest$NonStatic"); // Used to throw NullPointerException + } + + public static class DoesntExtendTestCase { + public static Test suite() { + return new TestSuite(); + } + } + + public void testInvokeSuiteOnNonSubclassOfTestCase() { + MockRunner runner= new MockRunner(); + runner.getTest(DoesntExtendTestCase.class.getName()); + assertFalse(runner.fRunFailed); + } +} diff --git a/junit4/src/test/java/junit/tests/runner/ResultTest.java b/junit4/src/test/java/junit/tests/runner/ResultTest.java new file mode 100644 index 0000000..ba3b509 --- /dev/null +++ b/junit4/src/test/java/junit/tests/runner/ResultTest.java @@ -0,0 +1,37 @@ +package junit.tests.runner; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import junit.framework.TestCase; +import junit.tests.framework.Success; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.tests.running.methods.AnnotationTest; + +public class ResultTest extends TestCase { + + public void testRunFailureResultCanBeSerialised() throws Exception { + JUnitCore runner = new JUnitCore(); + Result result = runner.run(AnnotationTest.FailureTest.class); + assertResultSerializable(result); + } + + public void testRunSuccessResultCanBeSerialised() throws Exception { + JUnitCore runner = new JUnitCore(); + Result result = runner.run(Success.class); + assertResultSerializable(result); + } + + private void assertResultSerializable(Result result) throws IOException, ClassNotFoundException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + new ObjectOutputStream(byteArrayOutputStream).writeObject(result); + byte[] bytes = byteArrayOutputStream.toByteArray(); + ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes)); + Result fromStream = (Result) objectInputStream.readObject(); + assertNotNull(fromStream); + } +} diff --git a/junit4/src/test/java/junit/tests/runner/StackFilterTest.java b/junit4/src/test/java/junit/tests/runner/StackFilterTest.java new file mode 100644 index 0000000..e70ea23 --- /dev/null +++ b/junit4/src/test/java/junit/tests/runner/StackFilterTest.java @@ -0,0 +1,46 @@ +package junit.tests.runner; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import junit.framework.TestCase; +import junit.runner.BaseTestRunner; + +public class StackFilterTest extends TestCase { + String fFiltered; + String fUnfiltered; + + @Override + protected void setUp() { + StringWriter swin= new StringWriter(); + PrintWriter pwin= new PrintWriter(swin); + pwin.println("junit.framework.AssertionFailedError"); + pwin.println(" at junit.framework.Assert.fail(Assert.java:144)"); + pwin.println(" at junit.framework.Assert.assert(Assert.java:19)"); + pwin.println(" at junit.framework.Assert.assert(Assert.java:26)"); + pwin.println(" at MyTest.f(MyTest.java:13)"); + pwin.println(" at MyTest.testStackTrace(MyTest.java:8)"); + pwin.println(" at java.lang.reflect.Method.invoke(Native Method)"); + pwin.println(" at junit.framework.TestCase.runTest(TestCase.java:156)"); + pwin.println(" at junit.framework.TestCase.runBare(TestCase.java:130)"); + pwin.println(" at junit.framework.TestResult$1.protect(TestResult.java:100)"); + pwin.println(" at junit.framework.TestResult.runProtected(TestResult.java:118)"); + pwin.println(" at junit.framework.TestResult.run(TestResult.java:103)"); + pwin.println(" at junit.framework.TestCase.run(TestCase.java:121)"); + pwin.println(" at junit.framework.TestSuite.runTest(TestSuite.java:157)"); + pwin.println(" at junit.framework.TestSuite.run(TestSuite.java, Compiled Code)"); + pwin.println(" at junit.swingui.TestRunner$17.run(TestRunner.java:669)"); + fUnfiltered= swin.toString(); + + StringWriter swout= new StringWriter(); + PrintWriter pwout= new PrintWriter(swout); + pwout.println("junit.framework.AssertionFailedError"); + pwout.println(" at MyTest.f(MyTest.java:13)"); + pwout.println(" at MyTest.testStackTrace(MyTest.java:8)"); + fFiltered= swout.toString(); + } + + public void testFilter() { + assertEquals(fFiltered, BaseTestRunner.getFilteredTrace(fUnfiltered)); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/runner/TextFeedbackTest.java b/junit4/src/test/java/junit/tests/runner/TextFeedbackTest.java new file mode 100644 index 0000000..fa2dbb2 --- /dev/null +++ b/junit4/src/test/java/junit/tests/runner/TextFeedbackTest.java @@ -0,0 +1,109 @@ + +package junit.tests.runner; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import junit.textui.ResultPrinter; +import junit.textui.TestRunner; + +public class TextFeedbackTest extends TestCase { + OutputStream output; + TestRunner runner; + + static class TestResultPrinter extends ResultPrinter { + TestResultPrinter(PrintStream writer) { + super(writer); + } + + /* Spoof printing time so the tests are deterministic + */ + @Override + protected String elapsedTimeAsString(long runTime) { + return "0"; + } + } + + public static void main(String[] args) { + TestRunner.run(TextFeedbackTest.class); + } + + @Override + public void setUp() { + output= new ByteArrayOutputStream(); + runner= new TestRunner(new TestResultPrinter(new PrintStream(output))); + } + + public void testEmptySuite() { + String expected= expected(new String[]{"", "Time: 0", "", "OK (0 tests)", ""}); + runner.doRun(new TestSuite()); + assertEquals(expected, output.toString()); + } + + + public void testOneTest() { + String expected= expected(new String[]{".", "Time: 0", "", "OK (1 test)", ""}); + TestSuite suite = new TestSuite(); + suite.addTest(new TestCase() { @Override + public void runTest() {}}); + runner.doRun(suite); + assertEquals(expected, output.toString()); + } + + public void testTwoTests() { + String expected= expected(new String[]{"..", "Time: 0", "", "OK (2 tests)", ""}); + TestSuite suite = new TestSuite(); + suite.addTest(new TestCase() { @Override + public void runTest() {}}); + suite.addTest(new TestCase() { @Override + public void runTest() {}}); + runner.doRun(suite); + assertEquals(expected, output.toString()); + } + + public void testFailure() { + String expected= expected(new String[]{".F", "Time: 0", "Failures here", "", "FAILURES!!!", "Tests run: 1, Failures: 1, Errors: 0", ""}); + ResultPrinter printer= new TestResultPrinter(new PrintStream(output)) { + @Override + public void printFailures(TestResult result) { + getWriter().println("Failures here"); + } + }; + runner.setPrinter(printer); + TestSuite suite = new TestSuite(); + suite.addTest(new TestCase() { @Override + public void runTest() {throw new AssertionFailedError();}}); + runner.doRun(suite); + assertEquals(expected, output.toString()); + } + + public void testError() { + String expected= expected(new String[]{".E", "Time: 0", "Errors here", "", "FAILURES!!!", "Tests run: 1, Failures: 0, Errors: 1", ""}); + ResultPrinter printer= new TestResultPrinter(new PrintStream(output)) { + @Override + public void printErrors(TestResult result) { + getWriter().println("Errors here"); + } + }; + runner.setPrinter(printer); + TestSuite suite = new TestSuite(); + suite.addTest(new TestCase() { @Override + public void runTest() throws Exception {throw new Exception();}}); + runner.doRun(suite); + assertEquals(expected, output.toString()); + } + + private String expected(String[] lines) { + OutputStream expected= new ByteArrayOutputStream(); + PrintStream expectedWriter= new PrintStream(expected); + for (int i= 0; i < lines.length; i++) + expectedWriter.println(lines[i]); + return expected.toString(); + } + +} diff --git a/junit4/src/test/java/junit/tests/runner/TextRunnerSingleMethodTest.java b/junit4/src/test/java/junit/tests/runner/TextRunnerSingleMethodTest.java new file mode 100644 index 0000000..1034fdd --- /dev/null +++ b/junit4/src/test/java/junit/tests/runner/TextRunnerSingleMethodTest.java @@ -0,0 +1,39 @@ +package junit.tests.runner; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import junit.framework.TestCase; +import junit.textui.ResultPrinter; +import junit.textui.TestRunner; + +/** + * Test invoking a single test method of a TestCase. + */ +public class TextRunnerSingleMethodTest extends TestCase { + + static boolean fgWasInvoked; + + public static class InvocationTest extends TestCase { + + public void testWasInvoked() { + TextRunnerSingleMethodTest.fgWasInvoked= true; + } + + public void testNotInvoked() { + fail("Shouldn't get here."); + } + } + + public void testSingle() throws Exception { + TestRunner t= new TestRunner(); + t.setPrinter(new ResultPrinter(new PrintStream(new ByteArrayOutputStream()))); + String[] args= { + "-m", "junit.tests.runner.TextRunnerSingleMethodTest$InvocationTest.testWasInvoked" + }; + fgWasInvoked= false; + t.start(args); + assertTrue(fgWasInvoked); + } + +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/runner/TextRunnerTest.java b/junit4/src/test/java/junit/tests/runner/TextRunnerTest.java new file mode 100644 index 0000000..d7b5941 --- /dev/null +++ b/junit4/src/test/java/junit/tests/runner/TextRunnerTest.java @@ -0,0 +1,61 @@ +package junit.tests.runner; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +public class TextRunnerTest extends TestCase { + + public void testFailure() throws Exception { + execTest("junit.tests.framework.Failure", false); + } + + public void testSuccess() throws Exception { + execTest("junit.tests.framework.Success", true); + } + + public void testError() throws Exception { + execTest("junit.tests.BogusDude", false); + } + + void execTest(String testClass, boolean success) throws Exception { + String java= System.getProperty("java.home")+File.separator+"bin"+File.separator+"java"; + String cp= System.getProperty("java.class.path"); + //use -classpath for JDK 1.1.7 compatibility + String [] cmd= { java, "-classpath", cp, "junit.textui.TestRunner", testClass}; + Process p= Runtime.getRuntime().exec(cmd); + InputStream i= p.getInputStream(); + while((i.read()) != -1) + ; //System.out.write(b); + assertTrue((p.waitFor() == 0) == success); + if (success) + assertTrue(p.exitValue() == 0); + else + assertFalse(p.exitValue() == 0); + } + + public void testRunReturnsResult() { + PrintStream oldOut= System.out; + System.setOut(new PrintStream ( + new OutputStream() { + @Override + public void write(int arg0) throws IOException { + } + } + )); + try { + TestResult result= junit.textui.TestRunner.run(new TestSuite()); + assertTrue(result.wasSuccessful()); + } finally { + System.setOut(oldOut); + } + } + + +}
\ No newline at end of file diff --git a/junit4/src/test/java/junit/tests/runner/package-info.java b/junit4/src/test/java/junit/tests/runner/package-info.java new file mode 100644 index 0000000..fc44e8a --- /dev/null +++ b/junit4/src/test/java/junit/tests/runner/package-info.java @@ -0,0 +1,4 @@ +/** + * Tests for the JUnit v3.x runner functionality. + */ +package junit.tests.runner;
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/samples/ListTest.java b/junit4/src/test/java/org/junit/samples/ListTest.java new file mode 100644 index 0000000..732037d --- /dev/null +++ b/junit4/src/test/java/org/junit/samples/ListTest.java @@ -0,0 +1,77 @@ +package org.junit.samples; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.JUnit4TestAdapter; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +/** + * A sample test case, testing {@link java.util.Vector}. + * + */ +public class ListTest { + protected List<Integer> fEmpty; + protected List<Integer> fFull; + protected static List<Integer> fgHeavy; + + public static void main (String... args) { + junit.textui.TestRunner.run (suite()); + } + + @BeforeClass public static void setUpOnce() { + fgHeavy= new ArrayList<Integer>(); + for(int i= 0; i < 1000; i++) + fgHeavy.add(i); + } + + @Before public void setUp() { + fEmpty= new ArrayList<Integer>(); + fFull= new ArrayList<Integer>(); + fFull.add(1); + fFull.add(2); + fFull.add(3); + } + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(ListTest.class); + } + @Ignore("not today") @Test public void capacity() { + int size= fFull.size(); + for (int i= 0; i < 100; i++) + fFull.add(i); + assertTrue(fFull.size() == 100+size); + } + + @Test public void testCopy() { + List<Integer> copy= new ArrayList<Integer>(fFull.size()); + copy.addAll(fFull); + assertTrue(copy.size() == fFull.size()); + assertTrue(copy.contains(1)); + } + + @Test public void contains() { + assertTrue(fFull.contains(1)); + assertTrue(!fEmpty.contains(1)); + } + @Test (expected=IndexOutOfBoundsException.class) public void elementAt() { + int i= fFull.get(0); + assertTrue(i == 1); + fFull.get(fFull.size()); // Should throw IndexOutOfBoundsException + } + + @Test public void removeAll() { + fFull.removeAll(fFull); + fEmpty.removeAll(fEmpty); + assertTrue(fFull.isEmpty()); + assertTrue(fEmpty.isEmpty()); + } + @Test public void removeElement() { + fFull.remove(new Integer(3)); + assertTrue(!fFull.contains(3)); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/samples/SimpleTest.java b/junit4/src/test/java/org/junit/samples/SimpleTest.java new file mode 100644 index 0000000..3202a7b --- /dev/null +++ b/junit4/src/test/java/org/junit/samples/SimpleTest.java @@ -0,0 +1,41 @@ +package org.junit.samples; + +import static org.junit.Assert.assertEquals; +import junit.framework.JUnit4TestAdapter; +import org.junit.Before; +import org.junit.Test; + +/** + * Some simple tests. + * + */ +public class SimpleTest { + protected int fValue1; + protected int fValue2; + + @Before public void setUp() { + fValue1= 2; + fValue2= 3; + } + + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(SimpleTest.class); + } + + public int unused; + @Test public void divideByZero() { + int zero= 0; + int result= 8/zero; + unused= result; // avoid warning for not using result + } + + @Test public void testEquals() { + assertEquals(12, 12); + assertEquals(12L, 12L); + assertEquals(new Long(12), new Long(12)); + + assertEquals("Size", 12, 13); + assertEquals("Capacity", 12.0, 11.99, 0.0); + } + +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/samples/money/MoneyTest.java b/junit4/src/test/java/org/junit/samples/money/MoneyTest.java new file mode 100644 index 0000000..f3f8317 --- /dev/null +++ b/junit4/src/test/java/org/junit/samples/money/MoneyTest.java @@ -0,0 +1,158 @@ +package org.junit.samples.money; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import junit.framework.JUnit4TestAdapter; +import junit.samples.money.IMoney; +import junit.samples.money.Money; +import junit.samples.money.MoneyBag; +import org.junit.Before; +import org.junit.Test; + +public class MoneyTest { + private Money f12CHF; + private Money f14CHF; + private Money f7USD; + private Money f21USD; + + private IMoney fMB1; + private IMoney fMB2; + + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(MoneyTest.class); + } + + @Before public void setUp() { + f12CHF= new Money(12, "CHF"); + f14CHF= new Money(14, "CHF"); + f7USD= new Money( 7, "USD"); + f21USD= new Money(21, "USD"); + + fMB1= MoneyBag.create(f12CHF, f7USD); + fMB2= MoneyBag.create(f14CHF, f21USD); + } + + @Test public void testBagMultiply() { + // {[12 CHF][7 USD]} *2 == {[24 CHF][14 USD]} + IMoney expected= MoneyBag.create(new Money(24, "CHF"), new Money(14, "USD")); + assertEquals(expected, fMB1.multiply(2)); + assertEquals(fMB1, fMB1.multiply(1)); + assertTrue(fMB1.multiply(0).isZero()); + } + @Test public void testBagNegate() { + // {[12 CHF][7 USD]} negate == {[-12 CHF][-7 USD]} + IMoney expected= MoneyBag.create(new Money(-12, "CHF"), new Money(-7, "USD")); + assertEquals(expected, fMB1.negate()); + } + @Test public void testBagSimpleAdd() { + // {[12 CHF][7 USD]} + [14 CHF] == {[26 CHF][7 USD]} + IMoney expected= MoneyBag.create(new Money(26, "CHF"), new Money(7, "USD")); + assertEquals(expected, fMB1.add(f14CHF)); + } + @Test public void testBagSubtract() { + // {[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]} + IMoney expected= MoneyBag.create(new Money(-2, "CHF"), new Money(-14, "USD")); + assertEquals(expected, fMB1.subtract(fMB2)); + } + @Test public void testBagSumAdd() { + // {[12 CHF][7 USD]} + {[14 CHF][21 USD]} == {[26 CHF][28 USD]} + IMoney expected= MoneyBag.create(new Money(26, "CHF"), new Money(28, "USD")); + assertEquals(expected, fMB1.add(fMB2)); + } + @Test public void testIsZero() { + assertTrue(fMB1.subtract(fMB1).isZero()); + assertTrue(MoneyBag.create(new Money (0, "CHF"), new Money (0, "USD")).isZero()); + } + @Test public void testMixedSimpleAdd() { + // [12 CHF] + [7 USD] == {[12 CHF][7 USD]} + IMoney expected= MoneyBag.create(f12CHF, f7USD); + assertEquals(expected, f12CHF.add(f7USD)); + } + @Test public void testBagNotEquals() { + IMoney bag= MoneyBag.create(f12CHF, f7USD); + assertFalse(bag.equals(new Money(12, "DEM").add(f7USD))); + } + @Test public void testMoneyBagEquals() { + assertTrue(!fMB1.equals(null)); + + assertEquals(fMB1, fMB1); + IMoney equal= MoneyBag.create(new Money(12, "CHF"), new Money(7, "USD")); + assertTrue(fMB1.equals(equal)); + assertTrue(!fMB1.equals(f12CHF)); + assertTrue(!f12CHF.equals(fMB1)); + assertTrue(!fMB1.equals(fMB2)); + } + @Test public void testMoneyBagHash() { + IMoney equal= MoneyBag.create(new Money(12, "CHF"), new Money(7, "USD")); + assertEquals(fMB1.hashCode(), equal.hashCode()); + } + @Test public void testMoneyEquals() { + assertTrue(!f12CHF.equals(null)); + Money equalMoney= new Money(12, "CHF"); + assertEquals(f12CHF, f12CHF); + assertEquals(f12CHF, equalMoney); + assertEquals(f12CHF.hashCode(), equalMoney.hashCode()); + assertTrue(!f12CHF.equals(f14CHF)); + } + @Test public void zeroMoniesAreEqualRegardlessOfCurrency() { + Money zeroDollars= new Money(0, "USD"); + Money zeroFrancs= new Money(0, "CHF"); + + assertEquals(zeroDollars, zeroFrancs); + assertEquals(zeroDollars.hashCode(), zeroFrancs.hashCode()); + } + @Test public void testMoneyHash() { + assertTrue(!f12CHF.equals(null)); + Money equal= new Money(12, "CHF"); + assertEquals(f12CHF.hashCode(), equal.hashCode()); + } + @Test public void testSimplify() { + IMoney money= MoneyBag.create(new Money(26, "CHF"), new Money(28, "CHF")); + assertEquals(new Money(54, "CHF"), money); + } + @Test public void testNormalize2() { + // {[12 CHF][7 USD]} - [12 CHF] == [7 USD] + Money expected= new Money(7, "USD"); + assertEquals(expected, fMB1.subtract(f12CHF)); + } + @Test public void testNormalize3() { + // {[12 CHF][7 USD]} - {[12 CHF][3 USD]} == [4 USD] + IMoney ms1= MoneyBag.create(new Money(12, "CHF"), new Money(3, "USD")); + Money expected= new Money(4, "USD"); + assertEquals(expected, fMB1.subtract(ms1)); + } + @Test public void testNormalize4() { // [12 CHF] - {[12 CHF][3 USD]} == [-3 USD] + IMoney ms1= MoneyBag.create(new Money(12, "CHF"), new Money(3, "USD")); + Money expected= new Money(-3, "USD"); + assertEquals(expected, f12CHF.subtract(ms1)); + } + @Test public void testPrint() { + assertEquals("[12 CHF]", f12CHF.toString()); + } + @Test public void testSimpleAdd() { + // [12 CHF] + [14 CHF] == [26 CHF] + Money expected= new Money(26, "CHF"); + assertEquals(expected, f12CHF.add(f14CHF)); + } + @Test public void testSimpleBagAdd() { + // [14 CHF] + {[12 CHF][7 USD]} == {[26 CHF][7 USD]} + IMoney expected= MoneyBag.create(new Money(26, "CHF"), new Money(7, "USD")); + assertEquals(expected, f14CHF.add(fMB1)); + } + @Test public void testSimpleMultiply() { + // [14 CHF] *2 == [28 CHF] + Money expected= new Money(28, "CHF"); + assertEquals(expected, f14CHF.multiply(2)); + } + @Test public void testSimpleNegate() { + // [14 CHF] negate == [-14 CHF] + Money expected= new Money(-14, "CHF"); + assertEquals(expected, f14CHF.negate()); + } + @Test public void testSimpleSubtract() { + // [14 CHF] - [12 CHF] == [2 CHF] + Money expected= new Money(2, "CHF"); + assertEquals(expected, f14CHF.subtract(f12CHF)); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/samples/money/package-info.java b/junit4/src/test/java/org/junit/samples/money/package-info.java new file mode 100644 index 0000000..2429514 --- /dev/null +++ b/junit4/src/test/java/org/junit/samples/money/package-info.java @@ -0,0 +1,7 @@ +/** + * JUnit v4.x Test for the Money example. + * + * @since 4.0 + * @see junit.samples.money.Money + */ +package org.junit.samples.money;
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/samples/package-info.java b/junit4/src/test/java/org/junit/samples/package-info.java new file mode 100644 index 0000000..ec52263 --- /dev/null +++ b/junit4/src/test/java/org/junit/samples/package-info.java @@ -0,0 +1,6 @@ +/** + * Provides examples on how to use JUnit 4. + * + * @since 4.0 + */ +package org.junit.samples;
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/AllTests.java b/junit4/src/test/java/org/junit/tests/AllTests.java new file mode 100644 index 0000000..2d459f1 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/AllTests.java @@ -0,0 +1,163 @@ +package org.junit.tests; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +import org.junit.tests.assertion.AssertionTest; +import org.junit.tests.assertion.BothTest; +import org.junit.tests.assertion.EachTest; +import org.junit.tests.assertion.MultipleFailureExceptionTest; +import org.junit.tests.deprecated.JUnit4ClassRunnerTest; +import org.junit.tests.description.AnnotatedDescriptionTest; +import org.junit.tests.description.SuiteDescriptionTest; +import org.junit.tests.description.TestDescriptionTest; +import org.junit.tests.experimental.AssumptionTest; +import org.junit.tests.experimental.AssumptionViolatedExceptionTest; +import org.junit.tests.experimental.ExperimentalTests; +import org.junit.tests.experimental.MatcherTest; +import org.junit.tests.experimental.categories.CategoriesAndParameterizedTest; +import org.junit.tests.experimental.categories.CategoryTest; +import org.junit.tests.experimental.max.JUnit38SortingTest; +import org.junit.tests.experimental.max.MaxStarterTest; +import org.junit.tests.experimental.parallel.ParallelClassTest; +import org.junit.tests.experimental.parallel.ParallelMethodTest; +import org.junit.tests.experimental.rules.BlockJUnit4ClassRunnerOverrideTest; +import org.junit.tests.experimental.rules.ClassRulesTest; +import org.junit.tests.experimental.rules.ExpectedExceptionRuleTest; +import org.junit.tests.experimental.rules.ExternalResourceRuleTest; +import org.junit.tests.experimental.rules.MethodRulesTest; +import org.junit.tests.experimental.rules.NameRulesTest; +import org.junit.tests.experimental.rules.RuleChainTest; +import org.junit.tests.experimental.rules.TempFolderRuleTest; +import org.junit.tests.experimental.rules.TestRuleTest; +import org.junit.tests.experimental.rules.TimeoutRuleTest; +import org.junit.tests.experimental.rules.VerifierRuleTest; +import org.junit.tests.experimental.theories.AllMembersSupplierTest; +import org.junit.tests.experimental.theories.runner.TheoriesPerformanceTest; +import org.junit.tests.junit3compatibility.AllTestsTest; +import org.junit.tests.junit3compatibility.ClassRequestTest; +import org.junit.tests.junit3compatibility.ForwardCompatibilityTest; +import org.junit.tests.junit3compatibility.InitializationErrorForwardCompatibilityTest; +import org.junit.tests.junit3compatibility.JUnit38ClassRunnerTest; +import org.junit.tests.junit3compatibility.OldTestClassAdaptingListenerTest; +import org.junit.tests.junit3compatibility.OldTests; +import org.junit.tests.junit3compatibility.SuiteMethodTest; +import org.junit.tests.listening.ListenerTest; +import org.junit.tests.listening.RunnerTest; +import org.junit.tests.listening.TestListenerTest; +import org.junit.tests.listening.TextListenerTest; +import org.junit.tests.listening.UserStopTest; +import org.junit.tests.manipulation.FilterTest; +import org.junit.tests.manipulation.FilterableTest; +import org.junit.tests.manipulation.SingleMethodTest; +import org.junit.tests.manipulation.SortableTest; +import org.junit.tests.running.classes.BlockJUnit4ClassRunnerTest; +import org.junit.tests.running.classes.EnclosedTest; +import org.junit.tests.running.classes.IgnoreClassTest; +import org.junit.tests.running.classes.ParameterizedTestTest; +import org.junit.tests.running.classes.ParentRunnerFilteringTest; +import org.junit.tests.running.classes.ParentRunnerTest; +import org.junit.tests.running.classes.RunWithTest; +import org.junit.tests.running.classes.SuiteTest; +import org.junit.tests.running.classes.TestClassTest; +import org.junit.tests.running.classes.UseSuiteAsASuperclassTest; +import org.junit.tests.running.core.CommandLineTest; +import org.junit.tests.running.core.JUnitCoreReturnsCorrectExitCodeTest; +import org.junit.tests.running.core.SystemExitTest; +import org.junit.tests.running.methods.AnnotationTest; +import org.junit.tests.running.methods.ExpectedTest; +import org.junit.tests.running.methods.InheritedTestTest; +import org.junit.tests.running.methods.ParameterizedTestMethodTest; +import org.junit.tests.running.methods.TestMethodTest; +import org.junit.tests.running.methods.TimeoutTest; +import org.junit.tests.validation.BadlyFormedClassesTest; +import org.junit.tests.validation.FailedConstructionTest; +import org.junit.tests.validation.InaccessibleBaseClassTest; +import org.junit.tests.validation.ValidationTest; + +// These test files need to be cleaned. See +// https://sourceforge.net/pm/task.php?func=detailtask&project_task_id=136507&group_id=15278&group_project_id=51407 + +@SuppressWarnings("deprecation") +@RunWith(Suite.class) +@SuiteClasses({ + AssumptionTest.class, + ClassRequestTest.class, + ListenerTest.class, + FailedConstructionTest.class, + TestDescriptionTest.class, + SuiteDescriptionTest.class, + AllTestsTest.class, + AnnotationTest.class, + AssertionTest.class, + CommandLineTest.class, + ExpectedTest.class, + MultipleFailureExceptionTest.class, + ForwardCompatibilityTest.class, + OldTests.class, + ParameterizedTestTest.class, + RunWithTest.class, + RunnerTest.class, + SuiteTest.class, + TestListenerTest.class, + TestMethodTest.class, + TextListenerTest.class, + TimeoutTest.class, + EnclosedTest.class, + ParameterizedTestMethodTest.class, + InitializationErrorForwardCompatibilityTest.class, + SingleMethodTest.class, + ValidationTest.class, + UserStopTest.class, + SortableTest.class, + JUnit38ClassRunnerTest.class, + SystemExitTest.class, + JUnitCoreReturnsCorrectExitCodeTest.class, + InaccessibleBaseClassTest.class, + SuiteMethodTest.class, + BadlyFormedClassesTest.class, + IgnoreClassTest.class, + OldTestClassAdaptingListenerTest.class, + AnnotatedDescriptionTest.class, + BothTest.class, + AssumptionViolatedExceptionTest.class, + EachTest.class, + ExperimentalTests.class, + InheritedTestTest.class, + TestClassTest.class, + AllMembersSupplierTest.class, + MatcherTest.class, + ObjectContractTest.class, + TheoriesPerformanceTest.class, + JUnit4ClassRunnerTest.class, + UseSuiteAsASuperclassTest.class, + FilterableTest.class, + FilterTest.class, + MaxStarterTest.class, + JUnit38SortingTest.class, + MethodRulesTest.class, + TestRuleTest.class, + TimeoutRuleTest.class, + ParallelClassTest.class, + ParallelMethodTest.class, + ParentRunnerTest.class, + NameRulesTest.class, + ClassRulesTest.class, + ExpectedExceptionRuleTest.class, + TempFolderRuleTest.class, + ExternalResourceRuleTest.class, + VerifierRuleTest.class, + CategoryTest.class, + CategoriesAndParameterizedTest.class, + ParentRunnerFilteringTest.class, + RuleChainTest.class, + BlockJUnit4ClassRunnerTest.class, + BlockJUnit4ClassRunnerOverrideTest.class +}) +public class AllTests { + public static Test suite() { + return new JUnit4TestAdapter(AllTests.class); + } +} diff --git a/junit4/src/test/java/org/junit/tests/ObjectContractTest.java b/junit4/src/test/java/org/junit/tests/ObjectContractTest.java new file mode 100644 index 0000000..1239527 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/ObjectContractTest.java @@ -0,0 +1,46 @@ +package org.junit.tests; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeThat; + +import java.lang.reflect.Method; + +import org.junit.Test; +import org.junit.Test.None; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; +import org.junit.runners.model.FrameworkMethod; + +@RunWith(Theories.class) +public class ObjectContractTest { + @DataPoints + public static Object[] objects= { new FrameworkMethod(toStringMethod()), + new FrameworkMethod(toStringMethod()), 3, null }; + + @Theory + @Test(expected= None.class) + public void equalsThrowsNoException(Object a, Object b) { + assumeNotNull(a); + a.equals(b); + } + + @Theory + public void equalsMeansEqualHashCodes(Object a, Object b) { + assumeNotNull(a, b); + assumeThat(a, is(b)); + assertThat(a.hashCode(), is(b.hashCode())); + } + + private static Method toStringMethod() { + try { + return Object.class.getMethod("toString"); + } catch (SecurityException e) { + } catch (NoSuchMethodException e) { + } + return null; + } +} diff --git a/junit4/src/test/java/org/junit/tests/TestSystem.java b/junit4/src/test/java/org/junit/tests/TestSystem.java new file mode 100644 index 0000000..9271d3b --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/TestSystem.java @@ -0,0 +1,31 @@ +package org.junit.tests; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +import org.junit.internal.JUnitSystem; + +public class TestSystem implements JUnitSystem { + private PrintStream out; + public int fCode; + private ByteArrayOutputStream fOutContents; + + public TestSystem() { + fOutContents= new ByteArrayOutputStream(); + out= new PrintStream(fOutContents); + } + + public void exit(int code) { + fCode= code; + } + + public PrintStream out() { + return out; + } + + public OutputStream outContents() { + return fOutContents; + } + +} diff --git a/junit4/src/test/java/org/junit/tests/assertion/AssertionTest.java b/junit4/src/test/java/org/junit/tests/assertion/AssertionTest.java new file mode 100644 index 0000000..dabf065 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/assertion/AssertionTest.java @@ -0,0 +1,486 @@ +package org.junit.tests.assertion; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.math.BigDecimal; + +import org.junit.Assert; +import org.junit.ComparisonFailure; +import org.junit.Test; +import org.junit.internal.ArrayComparisonFailure; + +/** + * Tests for {@link org.junit.Assert} + */ +public class AssertionTest { +// If you want to use 1.4 assertions, they will be reported correctly. +// However, you need to add the -ea VM argument when running. + +// @Test (expected=AssertionError.class) public void error() { +// assert false; +// } + + @Test(expected=AssertionError.class) public void fails() { + Assert.fail(); + } + + @Test public void failWithNoMessageToString() { + try { + Assert.fail(); + } catch (AssertionError exception) { + assertEquals("java.lang.AssertionError", exception.toString()); + } + } + + @Test public void failWithMessageToString() { + try { + Assert.fail("woops!"); + } catch (AssertionError exception) { + assertEquals("java.lang.AssertionError: woops!", exception.toString()); + } + } + + @Test(expected= AssertionError.class) public void arraysNotEqual() { + assertArrayEquals((new Object[] {new Object()}), (new Object[] {new Object()})); + } + + @Test(expected= AssertionError.class) public void arraysNotEqualWithMessage() { + assertArrayEquals("not equal", (new Object[] {new Object()}), (new Object[] {new Object()})); + } + + @Test public void arraysExpectedNullMessage() { + try { + assertArrayEquals("not equal", null, (new Object[] {new Object()})); + } catch (AssertionError exception) { + assertEquals("not equal: expected array was null", exception.getMessage()); + } + } + + @Test public void arraysActualNullMessage() { + try { + assertArrayEquals("not equal", (new Object[] {new Object()}), null); + } catch (AssertionError exception) { + assertEquals("not equal: actual array was null", exception.getMessage()); + } + } + + @Test public void arraysDifferentLengthMessage() { + try { + assertArrayEquals("not equal", (new Object[0]), (new Object[1])); + } catch (AssertionError exception) { + assertEquals("not equal: array lengths differed, expected.length=0 actual.length=1", exception.getMessage()); + } + } + + @Test(expected=ArrayComparisonFailure.class) public void arraysElementsDiffer() { + assertArrayEquals("not equal", (new Object[] {"this is a very long string in the middle of an array"}), (new Object[] {"this is another very long string in the middle of an array"})); + } + + @Test public void arraysDifferAtElement0nullMessage() { + try { + assertArrayEquals((new Object[] { true }), (new Object[] { false })); + } catch (AssertionError exception) { + assertEquals("arrays first differed at element [0]; expected:<true> but was:<false>", exception + .getMessage()); + } + } + + @Test public void arraysDifferAtElement1nullMessage() { + try { + assertArrayEquals((new Object[] { true, true }), (new Object[] { true, + false })); + } catch (AssertionError exception) { + assertEquals("arrays first differed at element [1]; expected:<true> but was:<false>", exception + .getMessage()); + } + } + + @Test public void arraysDifferAtElement0withMessage() { + try { + assertArrayEquals("message", (new Object[] { true }), (new Object[] { false })); + } catch (AssertionError exception) { + assertEquals("message: arrays first differed at element [0]; expected:<true> but was:<false>", exception + .getMessage()); + } + } + + @Test public void arraysDifferAtElement1withMessage() { + try { + assertArrayEquals("message", (new Object[] {true, true}), (new Object[] {true, false})); + fail(); + } catch (AssertionError exception) { + assertEquals("message: arrays first differed at element [1]; expected:<true> but was:<false>", exception.getMessage()); + } + } + + @Test public void multiDimensionalArraysAreEqual() { + assertArrayEquals((new Object[][]{{true, true}, {false, false}}), (new Object[][]{{true, true}, {false, false}})); + } + + @Test + public void multiDimensionalIntArraysAreEqual() { + int[][] int1= { { 1, 2, 3 }, { 4, 5, 6 } }; + int[][] int2= { { 1, 2, 3 }, { 4, 5, 6 } }; + assertArrayEquals(int1, int2); + } + + @Test + public void oneDimensionalPrimitiveArraysAreEqual() { + assertArrayEquals(new byte[] {1}, new byte[] {1}); + assertArrayEquals(new char[] {1}, new char[] {1}); + assertArrayEquals(new short[] {1}, new short[] {1}); + assertArrayEquals(new int[] {1}, new int[] {1}); + assertArrayEquals(new long[] {1}, new long[] {1}); + assertArrayEquals(new double[] {1.0}, new double[] {1.0}, 1.0); + assertArrayEquals(new float[] {1.0f}, new float[] {1.0f}, 1.0f); + } + + @Test(expected=AssertionError.class) + public void oneDimensionalDoubleArraysAreNotEqual() { + assertArrayEquals(new double[] {1.0}, new double[] {2.5}, 1.0); + } + + @Test(expected=AssertionError.class) + public void oneDimensionalFloatArraysAreNotEqual() { + assertArrayEquals(new float[] {1.0f}, new float[] {2.5f}, 1.0f); + } + + @Test(expected=AssertionError.class) + public void IntegerDoesNotEqualLong() { + assertEquals(new Integer(1), new Long(1)); + } + + @Test public void intsEqualLongs() { + assertEquals(1, 1L); + } + + @Test public void multiDimensionalArraysDeclaredAsOneDimensionalAreEqual() { + assertArrayEquals((new Object[]{new Object[] {true, true}, new Object[] {false, false}}), (new Object[]{new Object[] {true, true}, new Object[] {false, false}})); + } + + @Test public void multiDimensionalArraysAreNotEqual() { + try { + assertArrayEquals("message", (new Object[][]{{true, true}, {false, false}}), (new Object[][]{{true, true}, {true, false}})); + fail(); + } catch (AssertionError exception) { + assertEquals("message: arrays first differed at element [1][0]; expected:<false> but was:<true>", exception.getMessage()); + } + } + + @Test public void multiDimensionalArraysAreNotEqualNoMessage() { + try { + assertArrayEquals((new Object[][]{{true, true}, {false, false}}), (new Object[][]{{true, true}, {true, false}})); + fail(); + } catch (AssertionError exception) { + assertEquals("arrays first differed at element [1][0]; expected:<false> but was:<true>", exception.getMessage()); + } + } + + @Test public void arraysWithNullElementEqual() { + Object[] objects1 = new Object[] {null}; + Object[] objects2 = new Object[] {null}; + assertArrayEquals(objects1, objects2); + } + + @Test public void stringsDifferWithUserMessage() { + try { + assertEquals("not equal", "one", "two"); + } catch (Throwable exception) { + assertEquals("not equal expected:<[one]> but was:<[two]>", exception.getMessage()); + } + } + + @Test public void arraysEqual() { + Object element= new Object(); + Object[] objects1= new Object[] {element}; + Object[] objects2= new Object[] {element}; + assertArrayEquals(objects1, objects2); + } + + @Test public void arraysEqualWithMessage() { + Object element= new Object(); + Object[] objects1= new Object[] {element}; + Object[] objects2= new Object[] {element}; + assertArrayEquals("equal", objects1, objects2); + } + + @Test public void equals() { + Object o= new Object(); + assertEquals(o, o); + assertEquals("abc", "abc"); + assertEquals(true, true); + assertEquals((byte) 1, (byte) 1); + assertEquals('a', 'a'); + assertEquals((short) 1, (short) 1); + assertEquals(1, 1); // int by default, cast is unnecessary + assertEquals(1l, 1l); + assertEquals(1.0, 1.0, 0.0); + assertEquals(1.0d, 1.0d, 0.0d); + } + + @Test(expected= AssertionError.class) public void notEqualsObjectWithNull() { + assertEquals(new Object(), null); + } + + @Test(expected= AssertionError.class) public void notEqualsNullWithObject() { + assertEquals(null, new Object()); + } + + @Test public void notEqualsObjectWithNullWithMessage() { + Object o = new Object(); + try { + assertEquals("message", null, o); + fail(); + } + catch(AssertionError e) { + assertEquals("message expected:<null> but was:<" + o.toString() + ">", e.getMessage()); + } + } + + @Test public void notEqualsNullWithObjectWithMessage() { + Object o = new Object(); + try { + assertEquals("message", o, null); + fail(); + } + catch(AssertionError e) { + assertEquals("message expected:<"+ o.toString() + "> but was:<null>", e.getMessage()); + } + } + + @Test(expected= AssertionError.class) public void objectsNotEquals() { + assertEquals(new Object(), new Object()); + } + + @Test(expected= ComparisonFailure.class) public void stringsNotEqual() { + assertEquals("abc", "def"); + } + + @Test(expected= AssertionError.class) public void booleansNotEqual() { + assertEquals(true, false); + } + + @Test(expected= AssertionError.class) public void bytesNotEqual() { + assertEquals((byte) 1, (byte) 2); + } + + @Test(expected= AssertionError.class) public void charsNotEqual() { + assertEquals('a', 'b'); + } + + @Test(expected= AssertionError.class) public void shortsNotEqual() { + assertEquals((short) 1, (short) 2); + } + + @Test(expected= AssertionError.class) public void intsNotEqual() { + assertEquals(1, 2); + } + + @Test(expected= AssertionError.class) public void longsNotEqual() { + assertEquals(1l, 2l); + } + + @Test(expected= AssertionError.class) public void floatsNotEqual() { + assertEquals(1.0, 2.0, 0.9); + } + + @SuppressWarnings("deprecation") + @Test(expected= AssertionError.class) public void floatsNotEqualWithoutDelta() { + assertEquals(1.0, 1.1); + } + + @Test(expected= AssertionError.class) public void bigDecimalsNotEqual() { + assertEquals(new BigDecimal("123.4"), new BigDecimal("123.0")); + } + + + @Test(expected= AssertionError.class) public void doublesNotEqual() { + assertEquals(1.0d, 2.0d, 0.9d); + } + + @Test public void naNsAreEqual() { + assertEquals(Float.NaN, Float.NaN, Float.POSITIVE_INFINITY); + assertEquals(Double.NaN, Double.NaN, Double.POSITIVE_INFINITY); + } + + @Test public void same() { + Object o1= new Object(); + assertSame(o1, o1); + } + + @Test public void notSame() { + Object o1= new Object(); + Object o2= new Object(); + assertNotSame(o1, o2); + } + + @Test(expected= AssertionError.class) public void objectsNotSame() { + assertSame(new Object(), new Object()); + } + + @Test(expected= AssertionError.class) public void objectsAreSame() { + Object o= new Object(); + assertNotSame(o, o); + } + + @Test public void sameWithMessage() { + try { + assertSame("not same", "hello", "good-bye"); + fail(); + } catch (AssertionError exception) { + assertEquals("not same expected same:<hello> was not:<good-bye>", + exception.getMessage()); + } + } + + @Test public void sameNullMessage() { + try { + assertSame("hello", "good-bye"); + fail(); + } catch (AssertionError exception) { + assertEquals("expected same:<hello> was not:<good-bye>", exception.getMessage()); + } + } + + @Test public void notSameWithMessage() { + Object o= new Object(); + try { + assertNotSame("message", o, o); + fail(); + } catch (AssertionError exception) { + assertEquals("message expected not same", exception.getMessage()); + } + } + + @Test public void notSameNullMessage() { + Object o= new Object(); + try { + assertNotSame(o, o); + fail(); + } catch (AssertionError exception) { + assertEquals("expected not same", exception.getMessage()); + } + } + + @Test public void nullMessage() { + try { + fail(null); + } catch (AssertionError exception) { + // we used to expect getMessage() to return ""; see failWithNoMessageToString() + assertNull(exception.getMessage()); + } + } + + @Test public void nullMessageDisappearsWithStringAssertEquals() { + try { + assertEquals(null, "a", "b"); + fail(); + } catch (ComparisonFailure e) { + assertEquals("expected:<[a]> but was:<[b]>", e.getMessage()); + } + } + + @Test public void nullMessageDisappearsWithAssertEquals() { + try { + assertEquals(null, 1, 2); + fail(); + } catch (AssertionError e) { + assertEquals("expected:<1> but was:<2>", e.getMessage()); + } + } + + @Test(expected=AssertionError.class) + public void arraysDeclaredAsObjectAreComparedAsObjects() { + Object a1= new Object[] {"abc"}; + Object a2= new Object[] {"abc"}; + assertEquals(a1, a2); + } + + @Test public void implicitTypecastEquality() { + byte b = 1; + short s = 1; + int i = 1; + long l = 1L; + float f = 1.0f; + double d = 1.0; + + assertEquals(b, s); + assertEquals(b, i); + assertEquals(b, l); + assertEquals(s, i); + assertEquals(s, l); + assertEquals(i, l); + assertEquals(f, d, 0); + } + + @Test public void errorMessageDistinguishesDifferentValuesWithSameToString() { + try { + assertEquals("4", new Integer(4)); + } catch (AssertionError e) { + assertEquals("expected: java.lang.String<4> but was: java.lang.Integer<4>", e.getMessage()); + } + } + + @Test public void assertThatIncludesDescriptionOfTestedValueInErrorMessage() { + String expected = "expected"; + String actual = "actual"; + + String expectedMessage = "identifier\nExpected: \"expected\"\n got: \"actual\"\n"; + + try { + assertThat("identifier", actual, equalTo(expected)); + } catch (AssertionError e) { + assertEquals(expectedMessage, e.getMessage()); + } + } + + @Test public void assertThatIncludesAdvancedMismatch() { + String expectedMessage = "identifier\nExpected: is an instance of java.lang.Integer\n got: \"actual\"\n"; + + try { + assertThat("identifier", "actual", is(Integer.class)); + } catch (AssertionError e) { + assertEquals(expectedMessage, e.getMessage()); + } + } + + @Test public void assertThatDescriptionCanBeElided() { + String expected = "expected"; + String actual = "actual"; + + String expectedMessage = "\nExpected: \"expected\"\n got: \"actual\"\n"; + + try { + assertThat(actual, equalTo(expected)); + } catch (AssertionError e) { + assertEquals(expectedMessage, e.getMessage()); + } + } + + @Test public void nullAndStringNullPrintCorrectError() { + try { + assertEquals(null, "null"); + } catch (AssertionError e) { + assertEquals("expected: null<null> but was: java.lang.String<null>", e.getMessage()); + } + } + + @Test(expected=AssertionError.class) public void stringNullAndNullWorksToo() { + assertEquals("null", null); + } + + @Test(expected=AssertionError.class) + public void compareBigDecimalAndInteger() { + final BigDecimal bigDecimal = new BigDecimal("1.2"); + final Integer integer = Integer.valueOf("1"); + assertEquals(bigDecimal, integer); + } +} diff --git a/junit4/src/test/java/org/junit/tests/assertion/BothTest.java b/junit4/src/test/java/org/junit/tests/assertion/BothTest.java new file mode 100644 index 0000000..fd51a09 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/assertion/BothTest.java @@ -0,0 +1,71 @@ +package org.junit.tests.assertion; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeTrue; +import static org.junit.matchers.JUnitMatchers.both; +import static org.junit.matchers.JUnitMatchers.containsString; +import static org.junit.matchers.JUnitMatchers.either; +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +@RunWith(Theories.class) +public class BothTest { + @DataPoint + public static Matcher<Integer> IS_3= is(3); + + @DataPoint + public static Matcher<Integer> IS_4= is(4); + + @DataPoint + public static int THREE= 3; + + @Test + public void bothPasses() { + assertThat(3, both(is(Integer.class)).and(is(3))); + } + + @Theory + public void bothFails(int value, Matcher<Integer> first, + Matcher<Integer> second) { + assumeTrue(!(first.matches(value) && second.matches(value))); + assertThat(value, not(both(first).and(second))); + } + + @Theory + public <T> void descriptionIsSensible(Matcher<T> first, Matcher<T> second) { + Matcher<?> both= both(first).and(second); + assertThat(both.toString(), containsString(first.toString())); + assertThat(both.toString(), containsString(second.toString())); + } + + @Test + public void eitherPasses() { + assertThat(3, either(is(3)).or(is(4))); + } + + @Theory + public <T> void threeAndsWork(Matcher<Integer> first, + Matcher<Integer> second, Matcher<Integer> third, int value) { + assumeTrue(first.matches(value) && second.matches(value) + && third.matches(value)); + assertThat(value, both(first).and(second).and(third)); + } + + @Theory + public <T> void threeOrsWork(Matcher<Integer> first, + Matcher<Integer> second, Matcher<Integer> third, int value) { + assumeTrue(first.matches(value) || second.matches(value) + || third.matches(value)); + assertThat(value, either(first).or(second).or(third)); + } + + @Test public void subclassesAreOkInSecondPositionOnly() { + assertThat(3, both(is(Integer.class)).and(is(3))); + } +} diff --git a/junit4/src/test/java/org/junit/tests/assertion/EachTest.java b/junit4/src/test/java/org/junit/tests/assertion/EachTest.java new file mode 100644 index 0000000..16327e2 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/assertion/EachTest.java @@ -0,0 +1,13 @@ +package org.junit.tests.assertion; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import org.junit.Test; +import org.junit.internal.matchers.Each; + +public class EachTest { + @Test + public void eachDescription() { + assertThat(Each.each(is("a")).toString(), is("each is \"a\"")); + } +} diff --git a/junit4/src/test/java/org/junit/tests/assertion/MultipleFailureExceptionTest.java b/junit4/src/test/java/org/junit/tests/assertion/MultipleFailureExceptionTest.java new file mode 100644 index 0000000..1ddbcea --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/assertion/MultipleFailureExceptionTest.java @@ -0,0 +1,60 @@ +// Copyright 2010 Google Inc. All Rights Reserved. + +package org.junit.tests.assertion; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; +import org.junit.runners.model.MultipleFailureException; + +/** + * Tests for {@link org.junit.runners.model.MultipleFailureException} + * + * @author kcooney@google.com (Kevin Cooney) + */ +public class MultipleFailureExceptionTest { + + @Test + public void assertEmptyDoesNotThrowForEmptyList() throws Throwable { + MultipleFailureException.assertEmpty(Collections.<Throwable>emptyList()); + } + + @Test(expected=ExpectedException.class) + public void assertEmptyRethrowsSingleThrowable() throws Throwable { + MultipleFailureException.assertEmpty( + Collections.<Throwable>singletonList(new ExpectedException("pesto"))); + } + + @Test + public void assertEmptyThrowsMutipleFailureExceptionForManyThrowables() throws Throwable { + List<Throwable> errors = new ArrayList<Throwable>(); + errors.add(new ExpectedException("basil")); + errors.add(new RuntimeException("garlic")); + + try { + MultipleFailureException.assertEmpty(errors); + fail(); + } catch (MultipleFailureException expected) { + assertThat(expected.getFailures(), equalTo(errors)); + assertTrue(expected.getMessage().startsWith("There were 2 errors:\n")); + assertTrue(expected.getMessage().contains("ExpectedException(basil)\n")); + assertTrue(expected.getMessage().contains("RuntimeException(garlic)")); + } + } + + + private static class ExpectedException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public ExpectedException(String message) { + super(message); + } + } +} diff --git a/junit4/src/test/java/org/junit/tests/deprecated/JUnit4ClassRunnerTest.java b/junit4/src/test/java/org/junit/tests/deprecated/JUnit4ClassRunnerTest.java new file mode 100644 index 0000000..7cdfbb7 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/deprecated/JUnit4ClassRunnerTest.java @@ -0,0 +1,54 @@ +package org.junit.tests.deprecated; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import org.junit.Test; +import org.junit.internal.runners.JUnit4ClassRunner; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; + +/* + * @deprecated This is a simple smoke test to make sure the old JUnit4ClassRunner basically works. + * Delete this test when JUnit4ClassRunner goes to the Great Heap In The Sky. + */ +@Deprecated +public class JUnit4ClassRunnerTest { + + @SuppressWarnings("deprecation") + @RunWith(JUnit4ClassRunner.class) + public static class Example { + @Test public void success() {} + @Test public void failure() { + fail(); + } + } + + @Test + public void runWithOldJUnit4ClassRunner() { + Result result= JUnitCore.runClasses(Example.class); + assertThat(result.getRunCount(), is(2)); + assertThat(result.getFailureCount(), is(1)); + } + + @SuppressWarnings("deprecation") + @RunWith(JUnit4ClassRunner.class) + public static class UnconstructableExample { + public UnconstructableExample() { + throw new UnsupportedOperationException(); + } + @Test public void success() {} + @Test public void failure() { + fail(); + } + } + + + @Test + public void runWithOldJUnit4ClassRunnerAndBadConstructor() { + Result result= JUnitCore.runClasses(UnconstructableExample.class); + assertThat(result.getRunCount(), is(2)); + assertThat(result.getFailureCount(), is(2)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/description/AnnotatedDescriptionTest.java b/junit4/src/test/java/org/junit/tests/description/AnnotatedDescriptionTest.java new file mode 100644 index 0000000..370293b --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/description/AnnotatedDescriptionTest.java @@ -0,0 +1,96 @@ +package org.junit.tests.description; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.Request; + +public class AnnotatedDescriptionTest { + @Retention(RetentionPolicy.RUNTIME) + public @interface MyOwnAnnotation { + + } + + @MyOwnAnnotation + public static class AnnotatedClass { + @Test + public void a() { + } + } + + @Test + public void annotationsExistOnDescriptionsOfClasses() { + assertTrue((describe(AnnotatedClass.class).getAnnotation( + MyOwnAnnotation.class) != null)); + } + + @Test + public void getAnnotationsReturnsAllAnnotations() { + assertEquals(1, describe(ValueAnnotatedClass.class).getAnnotations() + .size()); + } + + @Ignore + public static class IgnoredClass { + @Test + public void a() { + } + } + + @Test + public void annotationsExistOnDescriptionsOfIgnoredClass() { + assertTrue((describe(IgnoredClass.class).getAnnotation(Ignore.class) != null)); + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface ValuedAnnotation { + String value(); + } + + @ValuedAnnotation("hello") + public static class ValueAnnotatedClass { + @Test + public void a() { + } + } + + @Test + public void descriptionOfTestClassHasValuedAnnotation() { + Description description= describe(ValueAnnotatedClass.class); + assertEquals("hello", description.getAnnotation(ValuedAnnotation.class) + .value()); + } + + @Test + public void childlessCopyOfDescriptionStillHasAnnotations() { + Description description= describe(ValueAnnotatedClass.class); + assertEquals("hello", description.childlessCopy().getAnnotation(ValuedAnnotation.class) + .value()); + } + + @Test + public void characterizeCreatingMyOwnAnnotation() { + Annotation annotation= new Ignore() { + public String value() { + return "message"; + } + + public Class<? extends Annotation> annotationType() { + return Ignore.class; + } + }; + + assertEquals(Ignore.class, annotation.annotationType()); + } + + private Description describe(Class<?> testClass) { + return Request.aClass(testClass).getRunner().getDescription(); + } +} diff --git a/junit4/src/test/java/org/junit/tests/description/SuiteDescriptionTest.java b/junit4/src/test/java/org/junit/tests/description/SuiteDescriptionTest.java new file mode 100644 index 0000000..8124b01 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/description/SuiteDescriptionTest.java @@ -0,0 +1,35 @@ +package org.junit.tests.description; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import org.junit.Test; +import org.junit.runner.Description; + +public class SuiteDescriptionTest { + Description childless = Description.createSuiteDescription("a"); + Description anotherChildless = Description.createSuiteDescription("a"); + Description namedB = Description.createSuiteDescription("b"); + + Description twoKids = descriptionWithTwoKids("foo", "bar"); + Description anotherTwoKids = descriptionWithTwoKids("foo", "baz"); + + @Test public void equalsIsCorrect() { + assertEquals(childless, anotherChildless); + assertFalse(childless.equals(namedB)); + assertEquals(childless, twoKids); + assertEquals(twoKids, anotherTwoKids); + assertFalse(twoKids.equals(new Integer(5))); + } + + @Test public void hashCodeIsReasonable() { + assertEquals(childless.hashCode(), anotherChildless.hashCode()); + assertFalse(childless.hashCode() == namedB.hashCode()); + } + + private Description descriptionWithTwoKids(String first, String second) { + Description twoKids = Description.createSuiteDescription("a"); + twoKids.addChild(Description.createTestDescription(getClass(), first)); + twoKids.addChild(Description.createTestDescription(getClass(), second)); + return twoKids; + } +} diff --git a/junit4/src/test/java/org/junit/tests/description/TestDescriptionTest.java b/junit4/src/test/java/org/junit/tests/description/TestDescriptionTest.java new file mode 100644 index 0000000..7d6c8a5 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/description/TestDescriptionTest.java @@ -0,0 +1,11 @@ +package org.junit.tests.description; + +import static org.junit.Assert.assertFalse; +import org.junit.Test; +import org.junit.runner.Description; + +public class TestDescriptionTest { + @Test public void equalsIsFalseForNonTestDescription() { + assertFalse(Description.createTestDescription(getClass(), "a").equals(new Integer(5))); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/AssumptionTest.java b/junit4/src/test/java/org/junit/tests/experimental/AssumptionTest.java new file mode 100644 index 0000000..3c0fd9c --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/AssumptionTest.java @@ -0,0 +1,177 @@ +package org.junit.tests.experimental; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeThat; +import static org.junit.Assume.assumeTrue; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import static org.junit.internal.matchers.StringContains.containsString; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.internal.AssumptionViolatedException; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +public class AssumptionTest { + public static class HasFailingAssumption { + @Test + public void assumptionsFail() { + assumeThat(3, is(4)); + fail(); + } + } + + @Test + public void failedAssumptionsMeanPassing() { + Result result= JUnitCore.runClasses(HasFailingAssumption.class); + assertThat(result.getRunCount(), is(1)); + assertThat(result.getIgnoreCount(), is(0)); + assertThat(result.getFailureCount(), is(0)); + } + + private static int assumptionFailures= 0; + @Test + public void failedAssumptionsCanBeDetectedByListeners() { + assumptionFailures= 0; + JUnitCore core= new JUnitCore(); + core.addListener(new RunListener() { + @Override + public void testAssumptionFailure(Failure failure) { + assumptionFailures++; + } + }); + core.run(HasFailingAssumption.class); + + assertThat(assumptionFailures, is(1)); + } + + public static class HasPassingAssumption { + @Test + public void assumptionsFail() { + assumeThat(3, is(3)); + fail(); + } + } + + @Test + public void passingAssumptionsScootThrough() { + Result result= JUnitCore.runClasses(HasPassingAssumption.class); + assertThat(result.getRunCount(), is(1)); + assertThat(result.getIgnoreCount(), is(0)); + assertThat(result.getFailureCount(), is(1)); + } + + @Test(expected= AssumptionViolatedException.class) + public void assumeThatWorks() { + assumeThat(1, is(2)); + } + + @Test + public void assumeThatPasses() { + assumeThat(1, is(1)); + assertCompletesNormally(); + } + + @Test + public void assumeThatPassesOnStrings() { + assumeThat("x", is("x")); + assertCompletesNormally(); + } + + @Test(expected= AssumptionViolatedException.class) + public void assumeNotNullThrowsException() { + Object[] objects= { 1, 2, null }; + assumeNotNull(objects); + } + + @Test + public void assumeNotNullPasses() { + Object[] objects= { 1, 2 }; + assumeNotNull(objects); + assertCompletesNormally(); + } + + @Test + public void assumeNotNullIncludesParameterList() { + try { + Object[] objects= { 1, 2, null }; + assumeNotNull(objects); + } catch (AssumptionViolatedException e) { + assertThat(e.getMessage(), containsString("1, 2, null")); + } catch (Exception e) { + fail("Should have thrown AssumptionViolatedException"); + } + } + @Test + public void assumeNoExceptionThrows() { + final Throwable exception= new NullPointerException(); + try { + assumeNoException(exception); + fail("Should have thrown exception"); + } catch (AssumptionViolatedException e) { + assertThat(e.getCause(), is(exception)); + } + } + + private void assertCompletesNormally() { + } + + @Test(expected=AssumptionViolatedException.class) public void assumeTrueWorks() { + Assume.assumeTrue(false); + } + + public static class HasFailingAssumeInBefore { + @Before public void checkForSomethingThatIsntThere() { + assumeTrue(false); + } + + @Test public void failing() { + fail(); + } + } + + @Test public void failingAssumptionInBeforePreventsTestRun() { + assertThat(testResult(HasFailingAssumeInBefore.class), isSuccessful()); + } + + public static class HasFailingAssumeInBeforeClass { + @BeforeClass public static void checkForSomethingThatIsntThere() { + assumeTrue(false); + } + + @Test public void failing() { + fail(); + } + } + + @Test public void failingAssumptionInBeforeClassIgnoresClass() { + assertThat(testResult(HasFailingAssumeInBeforeClass.class), isSuccessful()); + } + + public static class AssumptionFailureInConstructor { + public AssumptionFailureInConstructor() { + assumeTrue(false); + } + + @Test public void shouldFail() { + fail(); + } + } + + @Test public void failingAssumptionInConstructorIgnoresClass() { + assertThat(testResult(AssumptionFailureInConstructor.class), isSuccessful()); + } + + @Test(expected = IllegalArgumentException.class) + public void assumeWithExpectedException() { + assumeTrue(false); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/AssumptionViolatedExceptionTest.java b/junit4/src/test/java/org/junit/tests/experimental/AssumptionViolatedExceptionTest.java new file mode 100644 index 0000000..f1cb955 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/AssumptionViolatedExceptionTest.java @@ -0,0 +1,52 @@ +package org.junit.tests.experimental; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; +import static org.junit.internal.matchers.StringContains.containsString; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.internal.AssumptionViolatedException; +import org.junit.runner.RunWith; + +@RunWith(Theories.class) +public class AssumptionViolatedExceptionTest { + @DataPoint + public static Object TWO= 2; + + @DataPoint + public static Matcher<?> IS_THREE= is(3); + + @DataPoint + public static Matcher<?> NULL= null; + + @Theory + public void toStringReportsMatcher(Object actual, Matcher<?> matcher) { + assumeThat(matcher, notNullValue()); + assertThat(new AssumptionViolatedException(actual, matcher).toString(), + containsString(matcher.toString())); + } + + @Theory + public void toStringReportsValue(Object actual, Matcher<?> matcher) { + assertThat(new AssumptionViolatedException(actual, matcher).toString(), + containsString(String.valueOf(actual))); + } + + @Test + public void AssumptionViolatedExceptionDescribesItself() { + AssumptionViolatedException e= new AssumptionViolatedException(3, is(2)); + assertThat(StringDescription.asString(e), is("got: <3>, expected: is <2>")); + } + + @Test + public void simpleAssumptionViolatedExceptionDescribesItself() { + AssumptionViolatedException e= new AssumptionViolatedException("not enough money"); + assertThat(StringDescription.asString(e), is("failed assumption: not enough money")); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/ExperimentalTests.java b/junit4/src/test/java/org/junit/tests/experimental/ExperimentalTests.java new file mode 100644 index 0000000..7ac7714 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/ExperimentalTests.java @@ -0,0 +1,28 @@ +package org.junit.tests.experimental; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +import org.junit.tests.experimental.results.PrintableResultTest; +import org.junit.tests.experimental.results.ResultMatchersTest; +import org.junit.tests.experimental.theories.ParameterSignatureTest; +import org.junit.tests.experimental.theories.ParameterizedAssertionErrorTest; +import org.junit.tests.experimental.theories.extendingwithstubs.StubbedTheoriesTest; +import org.junit.tests.experimental.theories.runner.SuccessfulWithDataPointFields; +import org.junit.tests.experimental.theories.runner.UnsuccessfulWithDataPointFields; +import org.junit.tests.experimental.theories.runner.WhenNoParametersMatch; +import org.junit.tests.experimental.theories.runner.WithDataPointMethod; +import org.junit.tests.experimental.theories.runner.WithExtendedParameterSources; +import org.junit.tests.experimental.theories.runner.WithOnlyTestAnnotations; + +@RunWith(Suite.class) +@SuiteClasses( { ParameterizedAssertionErrorTest.class, + UnsuccessfulWithDataPointFields.class, + SuccessfulWithDataPointFields.class, PrintableResultTest.class, + ResultMatchersTest.class, WithDataPointMethod.class, + ParameterSignatureTest.class, WhenNoParametersMatch.class, + WithExtendedParameterSources.class, StubbedTheoriesTest.class, + WithOnlyTestAnnotations.class }) +public class ExperimentalTests { + +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/MatcherTest.java b/junit4/src/test/java/org/junit/tests/experimental/MatcherTest.java new file mode 100644 index 0000000..6b01179 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/MatcherTest.java @@ -0,0 +1,43 @@ +package org.junit.tests.experimental; + +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; +import static org.junit.experimental.results.ResultMatchers.hasFailureContaining; +import static org.junit.experimental.results.ResultMatchers.hasSingleFailureContaining; + +import java.util.Arrays; + +import org.hamcrest.Matcher; +import org.junit.experimental.results.PrintableResult; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; + +@RunWith(Theories.class) +public class MatcherTest { + @DataPoint + public static Matcher<?> SINGLE_FAILURE= hasSingleFailureContaining("cheese"); + + @DataPoint + public static Matcher<?> ANY_FAILURE= hasFailureContaining("cheese"); + + @DataPoint + public static PrintableResult TWO_FAILURES_ONE_CHEESE= new PrintableResult( + Arrays.asList(failure("cheese"), failure("mustard"))); + + @Theory + public <T> void differentMatchersHaveDifferentDescriptions( + Matcher<T> matcher1, Matcher<T> matcher2, T value) { + assumeThat(value, matcher1); + assumeThat(value, not(matcher2)); + assertThat(matcher1.toString(), not(matcher2.toString())); + } + + private static Failure failure(String string) { + return new Failure(Description.EMPTY, new Error(string)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/categories/CategoriesAndParameterizedTest.java b/junit4/src/test/java/org/junit/tests/experimental/categories/CategoriesAndParameterizedTest.java new file mode 100644 index 0000000..f589aea --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/categories/CategoriesAndParameterizedTest.java @@ -0,0 +1,123 @@ +package org.junit.tests.experimental.categories; + +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.hasFailureContaining; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; + +import java.util.Collection; +import java.util.Collections; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.experimental.categories.Categories; +import org.junit.experimental.categories.Categories.IncludeCategory; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Suite.SuiteClasses; + +public class CategoriesAndParameterizedTest { + public static class Token { + + } + + @RunWith(Parameterized.class) + public static class WellBehavedParameterizedTest { + public WellBehavedParameterizedTest(String a) { + } + + @Parameters + public static Collection<Object[]> getParameters() { + return Collections.singletonList(new Object[] { "a" }); + } + + @Test + public void testSomething() { + Assert.assertTrue(true); + } + } + + @RunWith(Parameterized.class) + public static class ParameterizedTestWithAttemptedMethodCategory { + public ParameterizedTestWithAttemptedMethodCategory(String a) { + } + + @Parameters + public static Collection<Object[]> getParameters() { + return Collections.singletonList(new Object[] { "a" }); + } + + @Test + @Category(Token.class) + public void testSomething() { + Assert.assertTrue(true); + } + } + + @RunWith(Parameterized.class) + @Category(Token.class) + public static class ParameterizedTestWithClassCategory { + public ParameterizedTestWithClassCategory(String a) { + } + + @Parameters + public static Collection<Object[]> getParameters() { + return Collections.singletonList(new Object[] { "a" }); + } + + @Test + public void testSomething() { + Assert.assertTrue(true); + } + } + + @Category(Token.class) + public static class VanillaCategorizedJUnitTest { + @Test + public void testSomething() { + Assert.assertTrue(true); + } + } + + @RunWith(Categories.class) + @IncludeCategory(Token.class) + @SuiteClasses({ VanillaCategorizedJUnitTest.class, + WellBehavedParameterizedTest.class, + ParameterizedTestWithClassCategory.class }) + public static class ParameterTokenSuiteWellFormed { + } + + @RunWith(Categories.class) + @IncludeCategory(Token.class) + @SuiteClasses({ ParameterizedTestWithAttemptedMethodCategory.class, VanillaCategorizedJUnitTest.class }) + public static class ParameterTokenSuiteMalformed { + } + + @RunWith(Categories.class) + @IncludeCategory(Token.class) + @SuiteClasses({ VanillaCategorizedJUnitTest.class, ParameterizedTestWithAttemptedMethodCategory.class }) + public static class ParameterTokenSuiteMalformedAndSwapped { + } + + @Test + public void shouldSucceedWithAParameterizedClassSomewhere() { + assertThat(testResult(ParameterTokenSuiteWellFormed.class), + isSuccessful()); + } + + @Test + public void shouldFailWithMethodLevelCategoryAnnotation() { + assertThat( + testResult(ParameterTokenSuiteMalformed.class), + hasFailureContaining("Category annotations on Parameterized classes are not supported on individual methods.")); + } + + @Test + public void shouldFailWithMethodLevelCategoryAnnotationSwapped() { + assertThat( + testResult(ParameterTokenSuiteMalformedAndSwapped.class), + hasFailureContaining("Category annotations on Parameterized classes are not supported on individual methods.")); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java b/junit4/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java new file mode 100644 index 0000000..b316731 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java @@ -0,0 +1,254 @@ +package org.junit.tests.experimental.categories; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import org.junit.Test; +import org.junit.experimental.categories.Categories; +import org.junit.experimental.categories.Category; +import org.junit.experimental.categories.Categories.CategoryFilter; +import org.junit.experimental.categories.Categories.ExcludeCategory; +import org.junit.experimental.categories.Categories.IncludeCategory; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +import org.junit.runners.model.InitializationError; + +public class CategoryTest { + public interface FastTests { + // category marker + } + + public interface SlowTests { + // category marker + } + + public static class A { + @Test + public void a() { + fail(); + } + + @Category(SlowTests.class) + @Test + public void b() { + } + } + + @Category(SlowTests.class) + public static class B { + @Test + public void c() { + + } + } + + public static class C { + @Test + public void d() { + fail(); + } + } + + @RunWith(Categories.class) + @IncludeCategory(SlowTests.class) + @SuiteClasses( { A.class, B.class, C.class }) + public static class SlowTestSuite { + } + + @RunWith(Categories.class) + @IncludeCategory(SlowTests.class) + @SuiteClasses( { A.class }) + public static class JustA { + } + + @Test + public void testCountOnJustA() { + assertThat(testResult(JustA.class), isSuccessful()); + } + + @Test + public void testCount() { + assertThat(testResult(SlowTestSuite.class), isSuccessful()); + } + + public static class Category1 {} + public static class Category2 {} + + public static class SomeAreSlow { + @Test public void noCategory() {} + @Category(Category1.class) @Test public void justCategory1() {} + @Category(Category2.class) @Test public void justCategory2() {} + @Category({Category1.class, Category2.class}) @Test public void both() {} + @Category({Category2.class, Category1.class}) @Test public void bothReversed() {} + } + + @RunWith(Categories.class) + @ExcludeCategory(Category1.class) + @SuiteClasses( { SomeAreSlow.class }) + public static class SomeAreSlowSuite { + } + + @Test + public void testCountOnAWithoutSlowTests() { + Result result= JUnitCore.runClasses(SomeAreSlowSuite.class); + assertThat(testResult(SomeAreSlowSuite.class), isSuccessful()); + assertEquals(2, result.getRunCount()); + assertTrue(result.wasSuccessful()); + } + + @RunWith(Categories.class) + @ExcludeCategory(Category1.class) + @IncludeCategory(Category2.class) + @SuiteClasses( { SomeAreSlow.class }) + public static class IncludeAndExcludeSuite { + } + + @Test + public void testsThatAreBothIncludedAndExcludedAreExcluded() { + Result result= JUnitCore.runClasses(IncludeAndExcludeSuite.class); + assertThat(testResult(SomeAreSlowSuite.class), isSuccessful()); + assertEquals(1, result.getRunCount()); + assertTrue(result.wasSuccessful()); + } + + @RunWith(Suite.class) + @SuiteClasses( { A.class, B.class, C.class }) + public static class TestSuiteWithNoCategories { + } + + @Test + public void testCountWithExplicitFilter() throws Throwable { + CategoryFilter include= CategoryFilter.include(SlowTests.class); + Request baseRequest= Request.aClass(TestSuiteWithNoCategories.class); + Result result= new JUnitCore().run(baseRequest.filterWith(include)); + assertTrue(result.wasSuccessful()); + assertEquals(2, result.getRunCount()); + } + + @Test + public void categoryFilterLeavesOnlyMatchingMethods() + throws InitializationError, NoTestsRemainException { + CategoryFilter filter= CategoryFilter.include(SlowTests.class); + BlockJUnit4ClassRunner runner= new BlockJUnit4ClassRunner(A.class); + filter.apply(runner); + assertEquals(1, runner.testCount()); + } + + public static class OneFastOneSlow { + @Category(FastTests.class) + @Test + public void a() { + + } + + @Category(SlowTests.class) + @Test + public void b() { + + } + } + + @Test + public void categoryFilterRejectsIncompatibleCategory() + throws InitializationError, NoTestsRemainException { + CategoryFilter filter= CategoryFilter.include(SlowTests.class); + BlockJUnit4ClassRunner runner= new BlockJUnit4ClassRunner( + OneFastOneSlow.class); + filter.apply(runner); + assertEquals(1, runner.testCount()); + } + + public static class OneFast { + @Category(FastTests.class) + @Test + public void a() { + + } + } + + @RunWith(Categories.class) + @IncludeCategory(SlowTests.class) + @SuiteClasses( { OneFast.class }) + public static class OneFastSuite { + } + + @Test + public void ifNoTestsToRunUseErrorRunner() { + Result result= JUnitCore.runClasses(OneFastSuite.class); + assertEquals(1, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + } + + @Test + public void describeACategoryFilter() { + CategoryFilter filter= CategoryFilter.include(SlowTests.class); + assertEquals("category " + SlowTests.class, filter.describe()); + } + + public static class OneThatIsBothFastAndSlow { + @Category({FastTests.class, SlowTests.class}) + @Test + public void a() { + + } + } + + @RunWith(Categories.class) + @IncludeCategory(SlowTests.class) + @SuiteClasses( { OneThatIsBothFastAndSlow.class }) + public static class ChooseSlowFromBoth { + } + + @Test public void runMethodWithTwoCategories() { + assertThat(testResult(ChooseSlowFromBoth.class), isSuccessful()); + } + + public interface VerySlowTests extends SlowTests { + + } + + public static class OneVerySlowTest { + @Category(VerySlowTests.class) + @Test + public void a() { + + } + } + + @RunWith(Categories.class) + @IncludeCategory(SlowTests.class) + @SuiteClasses( { OneVerySlowTest.class }) + public static class RunSlowFromVerySlow { + } + + @Test public void subclassesOfIncludedCategoriesAreRun() { + assertThat(testResult(RunSlowFromVerySlow.class), isSuccessful()); + } + + public static class ClassAsCategory { + + } + + public static class OneMoreTest { + @Category(ClassAsCategory.class) @Test public void a() {} + } + + @RunWith(Categories.class) + @IncludeCategory(ClassAsCategory.class) + @SuiteClasses( { OneMoreTest.class }) + public static class RunClassAsCategory { + } + + @Test public void classesCanBeCategories() { + assertThat(testResult(RunClassAsCategory.class), isSuccessful()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/max/DescriptionTest.java b/junit4/src/test/java/org/junit/tests/experimental/max/DescriptionTest.java new file mode 100644 index 0000000..c020320 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/max/DescriptionTest.java @@ -0,0 +1,24 @@ +package org.junit.tests.experimental.max; + +import static org.junit.Assert.assertNull; +import org.junit.Test; +import org.junit.runner.Description; + +public class DescriptionTest { + + @Test + public void parseClass_whenCantParse() { + assertNull(Description.TEST_MECHANISM.getTestClass()); + } + + @Test + public void parseMethod_whenCantParse() { + assertNull(Description.TEST_MECHANISM.getMethodName()); + } + + @Test(expected= IllegalArgumentException.class) + public void createSuiteDescription_whenZeroLength() { + Description.createSuiteDescription(""); + } + +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/max/JUnit38SortingTest.java b/junit4/src/test/java/org/junit/tests/experimental/max/JUnit38SortingTest.java new file mode 100644 index 0000000..68a25d6 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/max/JUnit38SortingTest.java @@ -0,0 +1,53 @@ +package org.junit.tests.experimental.max; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.util.List; + +import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.max.MaxCore; +import org.junit.runner.Description; +import org.junit.runner.Request; + +public class JUnit38SortingTest { + private MaxCore fMax; + private File fMaxFile; + + @Before + public void createMax() { + fMaxFile= new File("MaxCore.ser"); + if (fMaxFile.exists()) + fMaxFile.delete(); + fMax= MaxCore.storedLocally(fMaxFile); + } + + @After + public void forgetMax() { + fMaxFile.delete(); + } + + public static class JUnit4Test { + @Test public void pass() {} + } + + public static class JUnit38Test extends TestCase { + public void testFails() { fail(); } + public void testSucceeds() {} + public void testSucceedsToo() {} + } + + @Test + public void preferRecentlyFailed38Test() { + Request request= Request.classes(JUnit4Test.class, JUnit38Test.class); + fMax.run(request); + List<Description> tests= fMax.sortedLeavesForTest(request); + Description dontSucceed= Description.createTestDescription( + JUnit38Test.class, "testFails"); + assertEquals(dontSucceed, tests.get(0)); + } + +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/max/MaxStarterTest.java b/junit4/src/test/java/org/junit/tests/experimental/max/MaxStarterTest.java new file mode 100644 index 0000000..372af8f --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/max/MaxStarterTest.java @@ -0,0 +1,293 @@ +package org.junit.tests.experimental.max; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.matchers.JUnitMatchers.containsString; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.max.MaxCore; +import org.junit.internal.runners.JUnit38ClassRunner; +import org.junit.runner.Computer; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import org.junit.tests.AllTests; + +public class MaxStarterTest { + private MaxCore fMax; + + private File fMaxFile; + + @Before + public void createMax() { + fMaxFile= new File("MaxCore.ser"); + if (fMaxFile.exists()) + fMaxFile.delete(); + fMax= MaxCore.storedLocally(fMaxFile); + } + + @After + public void forgetMax() { + fMaxFile.delete(); + } + + public static class TwoTests { + @Test + public void succeed() { + } + + @Test + public void dontSucceed() { + fail(); + } + } + + @Test + public void twoTestsNotRunComeBackInRandomOrder() { + Request request= Request.aClass(TwoTests.class); + List<Description> things= fMax.sortedLeavesForTest(request); + Description succeed= Description.createTestDescription(TwoTests.class, + "succeed"); + Description dontSucceed= Description.createTestDescription( + TwoTests.class, "dontSucceed"); + assertTrue(things.contains(succeed)); + assertTrue(things.contains(dontSucceed)); + assertEquals(2, things.size()); + } + + @Test + public void preferNewTests() { + Request one= Request.method(TwoTests.class, "succeed"); + fMax.run(one); + Request two= Request.aClass(TwoTests.class); + List<Description> things= fMax.sortedLeavesForTest(two); + Description dontSucceed= Description.createTestDescription( + TwoTests.class, "dontSucceed"); + assertEquals(dontSucceed, things.get(0)); + assertEquals(2, things.size()); + } + + // This covers a seemingly-unlikely case, where you had a test that failed + // on the + // last run and you also introduced new tests. In such a case it pretty much + // doesn't matter + // which order they run, you just want them both to be early in the sequence + @Test + public void preferNewTestsOverTestsThatFailed() { + Request one= Request.method(TwoTests.class, "dontSucceed"); + fMax.run(one); + Request two= Request.aClass(TwoTests.class); + List<Description> things= fMax.sortedLeavesForTest(two); + Description succeed= Description.createTestDescription(TwoTests.class, + "succeed"); + assertEquals(succeed, things.get(0)); + assertEquals(2, things.size()); + } + + @Test + public void preferRecentlyFailed() { + Request request= Request.aClass(TwoTests.class); + fMax.run(request); + List<Description> tests= fMax.sortedLeavesForTest(request); + Description dontSucceed= Description.createTestDescription( + TwoTests.class, "dontSucceed"); + assertEquals(dontSucceed, tests.get(0)); + } + + @Test + public void sortTestsInMultipleClasses() { + Request request= Request.classes(Computer.serial(), TwoTests.class, + TwoTests.class); + fMax.run(request); + List<Description> tests= fMax.sortedLeavesForTest(request); + Description dontSucceed= Description.createTestDescription( + TwoTests.class, "dontSucceed"); + assertEquals(dontSucceed, tests.get(0)); + assertEquals(dontSucceed, tests.get(1)); + } + + public static class TwoUnEqualTests { + @Test + public void slow() throws InterruptedException { + Thread.sleep(100); + fail(); + } + + @Test + public void fast() { + fail(); + } + + } + + @Test + public void rememberOldRuns() { + fMax.run(TwoUnEqualTests.class); + + MaxCore reincarnation= MaxCore.storedLocally(fMaxFile); + List<Failure> failures= reincarnation.run(TwoUnEqualTests.class) + .getFailures(); + assertEquals("fast", failures.get(0).getDescription().getMethodName()); + assertEquals("slow", failures.get(1).getDescription().getMethodName()); + } + + @Test + public void preferFast() { + Request request= Request.aClass(TwoUnEqualTests.class); + fMax.run(request); + Description thing= fMax.sortedLeavesForTest(request).get(1); + assertEquals(Description.createTestDescription(TwoUnEqualTests.class, + "slow"), thing); + } + + @Test + public void listenersAreCalledCorrectlyInTheFaceOfFailures() + throws Exception { + JUnitCore core= new JUnitCore(); + final List<Failure> failures= new ArrayList<Failure>(); + core.addListener(new RunListener() { + @Override + public void testRunFinished(Result result) throws Exception { + failures.addAll(result.getFailures()); + } + }); + fMax.run(Request.aClass(TwoTests.class), core); + assertEquals(1, failures.size()); + } + + @Test + public void testsAreOnlyIncludedOnceWhenExpandingForSorting() + throws Exception { + Result result= fMax.run(Request.aClass(TwoTests.class)); + assertEquals(2, result.getRunCount()); + } + + public static class TwoOldTests extends TestCase { + public void testOne() { + } + + public void testTwo() { + } + } + + @Test + public void junit3TestsAreRunOnce() throws Exception { + Result result= fMax.run(Request.aClass(TwoOldTests.class), + new JUnitCore()); + assertEquals(2, result.getRunCount()); + } + + @Test + public void filterSingleMethodFromOldTestClass() throws Exception { + final Description method= Description.createTestDescription( + TwoOldTests.class, "testOne"); + Filter filter= Filter.matchMethodDescription(method); + JUnit38ClassRunner child= new JUnit38ClassRunner(TwoOldTests.class); + child.filter(filter); + assertEquals(1, child.testCount()); + } + + @Test + public void testCountsStandUpToFiltration() { + assertFilterLeavesTestUnscathed(AllTests.class); + } + + private void assertFilterLeavesTestUnscathed(Class<?> testClass) { + Request oneClass= Request.aClass(testClass); + Request filtered= oneClass.filterWith(new Filter() { + @Override + public boolean shouldRun(Description description) { + return true; + } + + @Override + public String describe() { + return "Everything"; + } + }); + + int filterCount= filtered.getRunner().testCount(); + int coreCount= oneClass.getRunner().testCount(); + assertEquals("Counts match up in " + testClass, coreCount, filterCount); + } + + private static class MalformedJUnit38Test { + private MalformedJUnit38Test() { + } + + @SuppressWarnings("unused") + public void testSucceeds() { + } + } + + @Test + public void maxShouldSkipMalformedJUnit38Classes() { + Request request= Request.aClass(MalformedJUnit38Test.class); + fMax.run(request); + } + + public static class MalformedJUnit38TestMethod extends TestCase { + @SuppressWarnings("unused") + private void testNothing() { + } + } + + String fMessage= null; + + @Test + public void correctErrorFromMalformedTest() { + Request request= Request.aClass(MalformedJUnit38TestMethod.class); + JUnitCore core= new JUnitCore(); + Request sorted= fMax.sortRequest(request); + Runner runner= sorted.getRunner(); + Result result= core.run(runner); + Failure failure= result.getFailures().get(0); + assertThat(failure.toString(), containsString("MalformedJUnit38TestMethod")); + assertThat(failure.toString(), containsString("testNothing")); + assertThat(failure.toString(), containsString("isn't public")); + } + + public static class HalfMalformedJUnit38TestMethod extends TestCase { + public void testSomething() { + } + + @SuppressWarnings("unused") + private void testNothing() { + } + } + + @Test + public void halfMalformed() { + assertThat(JUnitCore.runClasses(HalfMalformedJUnit38TestMethod.class) + .getFailureCount(), is(1)); + } + + + @Test + public void correctErrorFromHalfMalformedTest() { + Request request= Request.aClass(HalfMalformedJUnit38TestMethod.class); + JUnitCore core= new JUnitCore(); + Request sorted= fMax.sortRequest(request); + Runner runner= sorted.getRunner(); + Result result= core.run(runner); + Failure failure= result.getFailures().get(0); + assertThat(failure.toString(), containsString("MalformedJUnit38TestMethod")); + assertThat(failure.toString(), containsString("testNothing")); + assertThat(failure.toString(), containsString("isn't public")); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/parallel/ParallelClassTest.java b/junit4/src/test/java/org/junit/tests/experimental/parallel/ParallelClassTest.java new file mode 100644 index 0000000..3f9b2f1 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/parallel/ParallelClassTest.java @@ -0,0 +1,54 @@ +package org.junit.tests.experimental.parallel; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.experimental.ParallelComputer; +import org.junit.internal.matchers.TypeSafeMatcher; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + + +public class ParallelClassTest { + + public static class Example1 { + @Test public void one() throws InterruptedException { + Thread.sleep(1000); + } + } + public static class Example2 { + @Test public void one() throws InterruptedException { + Thread.sleep(1000); + } + } + + @RunWith(Suite.class) + @SuiteClasses({Example1.class, Example2.class}) + public static class ExampleSuite {} + + @Test(timeout=1500) public void testsRunInParallel() { + long start= System.currentTimeMillis(); + Result result= JUnitCore.runClasses(ParallelComputer.classes(), Example1.class, Example2.class); + assertTrue(result.wasSuccessful()); + long end= System.currentTimeMillis(); + assertThat(end - start, greaterThan(999)); // Overhead could be less than half a millisecond + } + + private Matcher<Long> greaterThan(final long l) { + return new TypeSafeMatcher<Long>() { + @Override + public boolean matchesSafely(Long item) { + return item > l; + } + + public void describeTo(Description description) { + description.appendText("greater than " + l); + } + }; + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/parallel/ParallelMethodTest.java b/junit4/src/test/java/org/junit/tests/experimental/parallel/ParallelMethodTest.java new file mode 100644 index 0000000..be5a792 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/parallel/ParallelMethodTest.java @@ -0,0 +1,44 @@ +package org.junit.tests.experimental.parallel; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.experimental.ParallelComputer; +import org.junit.internal.matchers.TypeSafeMatcher; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; + +public class ParallelMethodTest { + public static class Example { + @Test public void one() throws InterruptedException { + Thread.sleep(1000); + } + @Test public void two() throws InterruptedException { + Thread.sleep(1000); + } + } + + @Test public void testsRunInParallel() { + long start= System.currentTimeMillis(); + Result result= JUnitCore.runClasses(ParallelComputer.methods(), + Example.class); + assertTrue(result.wasSuccessful()); + long end= System.currentTimeMillis(); + assertThat(end - start, betweenInclusive(1000, 1900)); + } + + private Matcher<Long> betweenInclusive(final long min, final long max) { + return new TypeSafeMatcher<Long>() { + @Override + public boolean matchesSafely(Long item) { + return item >= min && item <= max; + } + + public void describeTo(Description description) { + description.appendText("between " + min + " and " + max); + } + }; + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/results/PrintableResultTest.java b/junit4/src/test/java/org/junit/tests/experimental/results/PrintableResultTest.java new file mode 100644 index 0000000..de715cb --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/results/PrintableResultTest.java @@ -0,0 +1,51 @@ +package org.junit.tests.experimental.results; + +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.allOf; +import static org.junit.Assert.assertThat; +import static org.junit.internal.matchers.StringContains.containsString; + +import java.util.Arrays; + +import org.junit.experimental.results.PrintableResult; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; + +@RunWith(Theories.class) +public class PrintableResultTest { + @SuppressWarnings("unchecked") + @Theory(nullsAccepted= false) + public void backTraceHasGoodToString(String descriptionName, + final String stackTraceClassName) { + Failure failure= new Failure(Description + .createSuiteDescription(descriptionName), new Throwable() { + private static final long serialVersionUID= 1L; + + @Override + public StackTraceElement[] getStackTrace() { + return new StackTraceElement[] { new StackTraceElement( + stackTraceClassName, "methodName", "fileName", 1) }; + } + }); + + assertThat(new PrintableResult(asList(failure)).toString(), allOf( + containsString(descriptionName), containsString(stackTraceClassName))); + } + + @DataPoint + public static String SHELL_POINT= "Shell Point"; + + @Theory + public void includeMultipleFailures(String secondExceptionName) { + PrintableResult backtrace= new PrintableResult(Arrays.asList( + new Failure(Description.createSuiteDescription("firstName"), + new RuntimeException("firstException")), new Failure( + Description.createSuiteDescription("secondName"), + new RuntimeException(secondExceptionName)))); + assertThat(backtrace.toString(), containsString(secondExceptionName)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/results/ResultMatchersTest.java b/junit4/src/test/java/org/junit/tests/experimental/results/ResultMatchersTest.java new file mode 100644 index 0000000..1263a8c --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/results/ResultMatchersTest.java @@ -0,0 +1,21 @@ +package org.junit.tests.experimental.results; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.internal.matchers.StringContains.containsString; +import org.junit.Test; +import org.junit.experimental.results.ResultMatchers; +import org.junit.experimental.theories.Theory; +public class ResultMatchersTest { + @Test + public void hasFailuresHasGoodDescription() { + assertThat(ResultMatchers.failureCountIs(3).toString(), + is("has 3 failures")); + } + + @Theory + public void hasFailuresDescriptionReflectsInput(int i) { + assertThat(ResultMatchers.failureCountIs(i).toString(), + containsString("" + i)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/BlockJUnit4ClassRunnerOverrideTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/BlockJUnit4ClassRunnerOverrideTest.java new file mode 100644 index 0000000..1fa3714 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/BlockJUnit4ClassRunnerOverrideTest.java @@ -0,0 +1,93 @@ +package org.junit.tests.experimental.rules; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; + +import java.util.LinkedList; +import java.util.List; + +import org.junit.Test; +import org.junit.rules.MethodRule; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; + +@SuppressWarnings("deprecation") +public class BlockJUnit4ClassRunnerOverrideTest { + public static class FlipBitRule implements MethodRule { + public Statement apply(final Statement base, FrameworkMethod method, + final Object target) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + target.getClass().getField("flipBit").set(target, true); + base.evaluate(); + } + }; + } + + } + + public static class OverrideRulesRunner extends BlockJUnit4ClassRunner { + public OverrideRulesRunner(Class<?> klass) throws InitializationError { + super(klass); + } + + @Override + protected List<MethodRule> rules(Object test) { + final LinkedList<MethodRule> methodRules= new LinkedList<MethodRule>( + super.rules(test)); + methodRules.add(new FlipBitRule()); + return methodRules; + } + } + + @RunWith(OverrideRulesRunner.class) + public static class OverrideRulesTest { + public boolean flipBit= false; + + @Test + public void testFlipBit() { + assertTrue(flipBit); + } + } + + @Test + public void overrideRulesMethod() { + assertThat(testResult(OverrideTestRulesTest.class), isSuccessful()); + } + + public static class OverrideTestRulesRunner extends BlockJUnit4ClassRunner { + public OverrideTestRulesRunner(Class<?> klass) + throws InitializationError { + super(klass); + } + + @Override + protected List<TestRule> getTestRules(final Object test) { + final LinkedList<TestRule> methodRules= new LinkedList<TestRule>( + super.getTestRules(test)); + methodRules.add(new TestRule() { + public Statement apply(Statement base, Description description) { + return new FlipBitRule().apply(base, null, test); + } + }); + return methodRules; + } + } + + @RunWith(OverrideTestRulesRunner.class) + public static class OverrideTestRulesTest extends OverrideRulesTest { + } + + @Test + public void overrideTestRulesMethod() { + assertThat(testResult(OverrideRulesTest.class), isSuccessful()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/ClassRulesTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/ClassRulesTest.java new file mode 100644 index 0000000..70b2d84 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/ClassRulesTest.java @@ -0,0 +1,101 @@ +/** + * Created Oct 19, 2009 + */ +package org.junit.tests.experimental.rules; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.ExternalResource; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runners.model.Statement; + +/** + * Tests to exercise class-level rules. + */ +public class ClassRulesTest { + public static class Counter extends ExternalResource { + public int count = 0; + + @Override + protected void before() throws Throwable { + count++; + } + } + + public static class ExampleTestWithClassRule { + @ClassRule + public static Counter counter= new Counter(); + + @Test + public void firstTest() { + assertEquals(1, counter.count); + } + + @Test + public void secondTest() { + assertEquals(1, counter.count); + } + } + + @Test + public void ruleIsAppliedOnce() { + ExampleTestWithClassRule.counter.count= 0; + JUnitCore.runClasses(ExampleTestWithClassRule.class); + assertEquals(1, ExampleTestWithClassRule.counter.count); + } + + public static class SubclassOfTestWithClassRule extends + ExampleTestWithClassRule { + + } + + @Test + public void ruleIsIntroducedAndEvaluatedOnSubclass() { + ExampleTestWithClassRule.counter.count= 0; + JUnitCore.runClasses(SubclassOfTestWithClassRule.class); + assertEquals(1, ExampleTestWithClassRule.counter.count); + } + + public static class CustomCounter implements TestRule { + public int count = 0; + + public Statement apply(final Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + count++; + base.evaluate(); + } + }; + } + } + + public static class ExampleTestWithCustomClassRule { + @ClassRule + public static CustomCounter counter= new CustomCounter(); + + @Test + public void firstTest() { + assertEquals(1, counter.count); + } + + @Test + public void secondTest() { + assertEquals(1, counter.count); + } + } + + + @Test + public void customRuleIsAppliedOnce() { + ExampleTestWithCustomClassRule.counter.count= 0; + Result result= JUnitCore.runClasses(ExampleTestWithCustomClassRule.class); + assertTrue(result.wasSuccessful()); + assertEquals(1, ExampleTestWithCustomClassRule.counter.count); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/ExpectedExceptionRuleTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/ExpectedExceptionRuleTest.java new file mode 100644 index 0000000..b5119cd --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/ExpectedExceptionRuleTest.java @@ -0,0 +1,232 @@ +package org.junit.tests.experimental.rules; + +import static org.hamcrest.CoreMatchers.any; +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.hasSingleFailureContaining; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import static org.junit.matchers.JUnitMatchers.both; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Rule; +import org.junit.Test; +import org.junit.internal.matchers.TypeSafeMatcher; +import org.junit.rules.ExpectedException; + +public class ExpectedExceptionRuleTest { + public static class HasExpectedException { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsNothing() { + + } + + @Test + public void throwsNullPointerException() { + thrown.expect(NullPointerException.class); + throw new NullPointerException(); + } + + @Test + public void throwsNullPointerExceptionWithMessage() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("happened?"); + throw new NullPointerException("What happened?"); + } + } + + @Test + public void expectedExceptionPasses() { + assertThat(testResult(HasExpectedException.class), isSuccessful()); + } + + public static class HasWrongExpectedException { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsNullPointerException() { + thrown.expect(NullPointerException.class); + throw new IllegalArgumentException(); + } + } + + @Test + public void unExpectedExceptionFails() { + assertThat( + testResult(HasWrongExpectedException.class), + hasSingleFailureContaining("Expected: an instance of java.lang.NullPointerException")); + } + + public static class HasWrongMessage { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsNullPointerException() { + thrown.expectMessage("expectedMessage"); + throw new IllegalArgumentException("actualMessage"); + } + } + + @Test + public void wrongMessageFails() { + assertThat( + testResult(HasWrongMessage.class), both( + hasSingleFailureContaining("expectedMessage")).and( + hasSingleFailureContaining("actualMessage"))); + } + + public static class WronglyExpectsException { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void doesntThrowNullPointerException() { + thrown.expect(NullPointerException.class); + } + } + + @Test + public void failsIfExceptionNeverComes() { + assertThat( + testResult(WronglyExpectsException.class), + hasSingleFailureContaining("Expected test to throw an instance of java.lang.NullPointerException")); + } + + public static class WronglyExpectsExceptionMessage { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void doesntThrowAnything() { + thrown.expectMessage("anything!"); + } + } + + @Test + public void failsIfExceptionMessageNeverComes() { + assertThat( + testResult(WronglyExpectsExceptionMessage.class), + hasSingleFailureContaining("Expected test to throw exception with message a string containing \"anything!\"")); + } + + public static class ExpectsSubstring { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsMore() { + thrown.expectMessage("anything!"); + throw new NullPointerException( + "This could throw anything! (as long as it has the right substring)"); + } + } + + @Test + public void passesWithSubstringMethod() { + assertThat(testResult(ExpectsSubstring.class), isSuccessful()); + } + + public static class ExpectsSubstringNullMessage { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsMore() { + thrown.expectMessage("anything!"); + throw new NullPointerException(); + } + } + + @Test + public void failsWithNullExceptionMessage() { + assertThat(testResult(ExpectsSubstringNullMessage.class), + hasSingleFailureContaining("NullPointerException")); + } + + public static class ExpectsMessageMatcher { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsMore() { + thrown.expectMessage(startsWith("Ack")); + throw new NullPointerException("Ack!"); + } + } + + @Test + public void succeedsWithMessageMatcher() { + assertThat(testResult(ExpectsMessageMatcher.class), isSuccessful()); + } + + public static class ExpectedMessageMatcherFails { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsMore() { + thrown.expectMessage(startsWith("Wrong start")); + throw new NullPointerException("Back!"); + } + } + + + + private static Matcher<String> startsWith(final String prefix) { + return new TypeSafeMatcher<String>() { + public void describeTo(Description description) { + description.appendText("starts with "); + description.appendText(prefix); + } + + @Override + public boolean matchesSafely(String item) { + return item.startsWith(prefix); + } + }; + } + + @Test + public void failsWithMatcher() { + assertThat(testResult(ExpectedMessageMatcherFails.class), + hasSingleFailureContaining("Wrong start")); + } + + public static class ExpectsMatcher { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsMore() { + thrown.expect(any(Throwable.class)); + throw new NullPointerException("Ack!"); + } + } + + @Test + public void succeedsWithMatcher() { + assertThat(testResult(ExpectsMatcher.class), isSuccessful()); + } + + public static class ExpectsMultipleMatchers { + @Rule + public ExpectedException thrown= ExpectedException.none(); + + @Test + public void throwsMore() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Ack!"); + throw new NullPointerException("Ack!"); + } + } + + @Test + public void failsWithMultipleMatchers() { + assertThat(testResult(ExpectsMultipleMatchers.class), + hasSingleFailureContaining("IllegalArgumentException")); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/ExternalResourceRuleTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/ExternalResourceRuleTest.java new file mode 100644 index 0000000..48cbeb9 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/ExternalResourceRuleTest.java @@ -0,0 +1,37 @@ +package org.junit.tests.experimental.rules; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExternalResource; + +public class ExternalResourceRuleTest { + private static String callSequence; + + public static class UsesExternalResource { + @Rule public ExternalResource resource = new ExternalResource() { + @Override + protected void before() throws Throwable { + callSequence += "before "; + }; + + @Override + protected void after() { + callSequence += "after "; + }; + }; + + @Test public void testFoo() { + callSequence += "test "; + } + } + + @Test public void externalResourceGeneratesCorrectSequence() { + callSequence= ""; + assertThat(testResult(UsesExternalResource.class), isSuccessful()); + assertEquals("before test after ", callSequence); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/LoggingTestWatcher.java b/junit4/src/test/java/org/junit/tests/experimental/rules/LoggingTestWatcher.java new file mode 100644 index 0000000..98251b9 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/LoggingTestWatcher.java @@ -0,0 +1,32 @@ +package org.junit.tests.experimental.rules; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +class LoggingTestWatcher extends TestWatcher { + private final StringBuilder log; + + LoggingTestWatcher(StringBuilder log) { + this.log= log; + } + + @Override + protected void succeeded(Description description) { + log.append("succeeded "); + } + + @Override + protected void failed(Throwable e, Description description) { + log.append("failed "); + } + + @Override + protected void starting(Description description) { + log.append("starting "); + } + + @Override + protected void finished(Description description) { + log.append("finished "); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/MethodRulesTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/MethodRulesTest.java new file mode 100644 index 0000000..116953e --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/MethodRulesTest.java @@ -0,0 +1,275 @@ +package org.junit.tests.experimental.rules; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.hasSingleFailureContaining; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import static org.junit.matchers.JUnitMatchers.containsString; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.MethodRule; +import org.junit.rules.TestName; +import org.junit.rules.TestWatchman; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +@SuppressWarnings("deprecation") +public class MethodRulesTest { + private static boolean wasRun; + + public static class ExampleTest { + @Rule + public MethodRule example= new MethodRule() { + public Statement apply(final Statement base, + FrameworkMethod method, Object target) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + wasRun= true; + base.evaluate(); + }; + }; + } + }; + + @Test + public void nothing() { + + } + } + + @Test + public void ruleIsIntroducedAndEvaluated() { + wasRun= false; + JUnitCore.runClasses(ExampleTest.class); + assertTrue(wasRun); + } + + public static class SonOfExampleTest extends ExampleTest { + + } + + @Test + public void ruleIsIntroducedAndEvaluatedOnSubclass() { + wasRun= false; + JUnitCore.runClasses(SonOfExampleTest.class); + assertTrue(wasRun); + } + + private static int runCount; + + public static class MultipleRuleTest { + private static class Increment implements MethodRule { + public Statement apply(final Statement base, + FrameworkMethod method, Object target) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + runCount++; + base.evaluate(); + }; + }; + } + } + + @Rule + public MethodRule incrementor1= new Increment(); + + @Rule + public MethodRule incrementor2= new Increment(); + + @Test + public void nothing() { + + } + } + + @Test + public void multipleRulesAreRun() { + runCount= 0; + JUnitCore.runClasses(MultipleRuleTest.class); + assertEquals(2, runCount); + } + + public static class NoRulesTest { + public int x; + + @Test + public void nothing() { + + } + } + + @Test + public void ignoreNonRules() { + Result result= JUnitCore.runClasses(NoRulesTest.class); + assertEquals(0, result.getFailureCount()); + } + + private static String log; + + public static class OnFailureTest { + @Rule + public MethodRule watchman= new TestWatchman() { + @Override + public void failed(Throwable e, FrameworkMethod method) { + log+= method.getName() + " " + e.getClass().getSimpleName(); + } + }; + + @Test + public void nothing() { + fail(); + } + } + + @Test + public void onFailure() { + log= ""; + Result result= JUnitCore.runClasses(OnFailureTest.class); + assertEquals("nothing AssertionError", log); + assertEquals(1, result.getFailureCount()); + } + + public static class WatchmanTest { + private static String watchedLog; + + @Rule + public MethodRule watchman= new TestWatchman() { + @Override + public void failed(Throwable e, FrameworkMethod method) { + watchedLog+= method.getName() + " " + + e.getClass().getSimpleName() + "\n"; + } + + @Override + public void succeeded(FrameworkMethod method) { + watchedLog+= method.getName() + " " + "success!\n"; + } + }; + + @Test + public void fails() { + fail(); + } + + @Test + public void succeeds() { + } + } + + @Test + public void succeeded() { + WatchmanTest.watchedLog= ""; + JUnitCore.runClasses(WatchmanTest.class); + assertThat(WatchmanTest.watchedLog, containsString("fails AssertionError")); + assertThat(WatchmanTest.watchedLog, containsString("succeeds success!")); + } + + public static class BeforesAndAfters { + private static String watchedLog; + + @Before public void before() { + watchedLog+= "before "; + } + + @Rule + public MethodRule watchman= new TestWatchman() { + @Override + public void starting(FrameworkMethod method) { + watchedLog+= "starting "; + } + + @Override + public void finished(FrameworkMethod method) { + watchedLog+= "finished "; + } + + @Override + public void succeeded(FrameworkMethod method) { + watchedLog+= "succeeded "; + } + }; + + @After public void after() { + watchedLog+= "after "; + } + + @Test + public void succeeds() { + watchedLog+= "test "; + } + } + + @Test + public void beforesAndAfters() { + BeforesAndAfters.watchedLog= ""; + JUnitCore.runClasses(BeforesAndAfters.class); + assertThat(BeforesAndAfters.watchedLog, is("starting before test after succeeded finished ")); + } + + public static class WrongTypedField { + @Rule public int x = 5; + @Test public void foo() {} + } + + @Test public void validateWrongTypedField() { + assertThat(testResult(WrongTypedField.class), + hasSingleFailureContaining("must implement MethodRule")); + } + + public static class SonOfWrongTypedField extends WrongTypedField { + + } + + @Test public void validateWrongTypedFieldInSuperclass() { + assertThat(testResult(SonOfWrongTypedField.class), + hasSingleFailureContaining("must implement MethodRule")); + } + + public static class PrivateRule { + @SuppressWarnings("unused") + @Rule private TestRule rule = new TestName(); + @Test public void foo() {} + } + + @Test public void validatePrivateRule() { + assertThat(testResult(PrivateRule.class), + hasSingleFailureContaining("must be public")); + } + + public static class CustomTestName implements TestRule { + public String name = null; + + public Statement apply(final Statement base, final Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + name = description.getMethodName(); + base.evaluate(); + } + }; + } + } + + public static class UsesCustomMethodRule { + @Rule public CustomTestName counter = new CustomTestName(); + @Test public void foo() { + assertEquals("foo", counter.name); + } + } + + @Test public void useCustomMethodRule() { + assertThat(testResult(UsesCustomMethodRule.class), isSuccessful()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/NameRulesTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/NameRulesTest.java new file mode 100644 index 0000000..4f15bde --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/NameRulesTest.java @@ -0,0 +1,50 @@ +package org.junit.tests.experimental.rules; + +import static org.junit.Assert.assertEquals; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; + +@RunWith(Enclosed.class) +public class NameRulesTest { + public static class TestNames { + @Rule + public TestName name= new TestName(); + + @Test + public void testA() { + assertEquals("testA", name.getMethodName()); + } + + @Test + public void testB() { + assertEquals("testB", name.getMethodName()); + } + } + + public static class BeforeAndAfterTest { + @Rule + public TestName name= new TestName(); + + private final String expectedName= "x"; + + @Before + public void setUp() { + assertEquals(expectedName, name.getMethodName()); + } + + @Test + public void x() { + assertEquals(expectedName, name.getMethodName()); + } + + @After + public void tearDown() { + assertEquals(expectedName, name.getMethodName()); + } + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/RuleChainTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/RuleChainTest.java new file mode 100644 index 0000000..e1ea4c5 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/RuleChainTest.java @@ -0,0 +1,60 @@ +package org.junit.tests.experimental.rules; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.rules.RuleChain.outerRule; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +public class RuleChainTest { + private static final List<String> LOG= new ArrayList<String>(); + + private static class LoggingRule extends TestWatcher { + private final String label; + + public LoggingRule(String label) { + this.label= label; + } + + @Override + protected void starting(Description description) { + LOG.add("starting " + label); + } + + @Override + protected void finished(Description description) { + LOG.add("finished " + label); + } + } + + public static class UseRuleChain { + @Rule + public final RuleChain chain= outerRule(new LoggingRule("outer rule")) + .around(new LoggingRule("middle rule")).around( + new LoggingRule("inner rule")); + + @Test + public void example() { + assertTrue(true); + } + } + + @Test + public void executeRulesInCorrectOrder() throws Exception { + testResult(UseRuleChain.class); + List<String> expectedLog= asList("starting outer rule", + "starting middle rule", "starting inner rule", + "finished inner rule", "finished middle rule", + "finished outer rule"); + assertEquals(expectedLog, LOG); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/RuleFieldValidatorTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/RuleFieldValidatorTest.java new file mode 100644 index 0000000..fa6dcbf --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/RuleFieldValidatorTest.java @@ -0,0 +1,96 @@ +package org.junit.tests.experimental.rules; + +import static org.junit.Assert.assertEquals; +import static org.junit.internal.runners.rules.RuleFieldValidator.CLASS_RULE_VALIDATOR; +import static org.junit.internal.runners.rules.RuleFieldValidator.RULE_VALIDATOR; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.MethodRule; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestRule; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +@SuppressWarnings("deprecation") +public class RuleFieldValidatorTest { + private final List<Throwable> errors = new ArrayList<Throwable>(); + + @Test + public void rejectProtectedClassRule() { + TestClass target= new TestClass(TestWithProtectedClassRule.class); + CLASS_RULE_VALIDATOR.validate(target, errors); + assertOneErrorWithMessage("The @ClassRule 'temporaryFolder' must be public."); + } + + public static class TestWithProtectedClassRule { + @ClassRule + protected static TestRule temporaryFolder = new TemporaryFolder(); + } + + @Test + public void rejectNonStaticClassRule() { + TestClass target= new TestClass(TestWithNonStaticClassRule.class); + CLASS_RULE_VALIDATOR.validate(target, errors); + assertOneErrorWithMessage("The @ClassRule 'temporaryFolder' must be static."); + } + + public static class TestWithNonStaticClassRule { + @ClassRule + public TestRule temporaryFolder = new TemporaryFolder(); + } + + @Test + public void acceptNonStaticTestRule() { + TestClass target= new TestClass(TestWithNonStaticTestRule.class); + RULE_VALIDATOR.validate(target, errors); + assertNumberOfErrors(0); + } + + public static class TestWithNonStaticTestRule { + @Rule + public TestRule temporaryFolder = new TemporaryFolder(); + } + + @Test + public void acceptMethodRule() throws Exception { + TestClass target= new TestClass(TestWithMethodRule.class); + RULE_VALIDATOR.validate(target, errors); + assertNumberOfErrors(0); + } + + public static class TestWithMethodRule { + @Rule + public MethodRule temporaryFolder = new MethodRule(){ + public Statement apply(Statement base, FrameworkMethod method, + Object target) { + return null; + }}; + } + + @Test + public void rejectArbitraryObjectWithRuleAnnotation() throws Exception { + TestClass target= new TestClass(TestWithArbitraryObjectWithRuleAnnotation.class); + RULE_VALIDATOR.validate(target, errors); + assertOneErrorWithMessage("The @Rule 'arbitraryObject' must implement MethodRule or TestRule."); + } + + public static class TestWithArbitraryObjectWithRuleAnnotation { + @Rule + public Object arbitraryObject = 1; + } + + private void assertOneErrorWithMessage(String message) { + assertNumberOfErrors(1); + assertEquals("Wrong error message:", message, errors.get(0).getMessage()); + } + + private void assertNumberOfErrors(int numberOfErrors) { + assertEquals("Wrong number of errors:", numberOfErrors, errors.size()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java new file mode 100644 index 0000000..200d51e --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java @@ -0,0 +1,200 @@ +package org.junit.tests.experimental.rules; + +import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.failureCountIs; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import static org.junit.internal.matchers.IsCollectionContaining.hasItem; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class TempFolderRuleTest { + private static File[] createdFiles= new File[20]; + + public static class HasTempFolder { + @Rule + public TemporaryFolder folder= new TemporaryFolder(); + + @Test + public void testUsingTempFolder() throws IOException { + createdFiles[0]= folder.newFile("myfile.txt"); + assertTrue(createdFiles[0].exists()); + } + } + + @Test + public void tempFolderIsDeleted() { + assertThat(testResult(HasTempFolder.class), isSuccessful()); + assertFalse(createdFiles[0].exists()); + } + + public static class CreatesSubFolder { + @Rule + public TemporaryFolder folder= new TemporaryFolder(); + + @Test + public void testUsingTempFolder() throws IOException { + String subfolder = "subfolder"; + String filename = "a.txt"; + createdFiles[0]= folder.newFolder(subfolder); + new File(createdFiles[0], filename).createNewFile(); + + File expectedFile = new File(folder.getRoot(), join(subfolder, filename)); + + assertTrue(expectedFile.exists()); + } + + @Test + public void testUsingTempTreeFolders() throws IOException { + String subfolder = "subfolder"; + String anotherfolder = "anotherfolder"; + String filename = "a.txt"; + + createdFiles[0] = folder.newFolder(subfolder, anotherfolder); + new File(createdFiles[0], filename).createNewFile(); + + File expectedFile = new File(folder.getRoot(), join(subfolder, anotherfolder, filename)); + + assertTrue(expectedFile.exists()); + } + + private String join(String... folderNames) { + StringBuilder path = new StringBuilder(); + for (String folderName : folderNames) { + path.append(File.separator).append(folderName); + } + return path.toString(); + } + } + + @Test + public void subFolderIsDeleted() { + assertThat(testResult(CreatesSubFolder.class), isSuccessful()); + assertFalse(createdFiles[0].exists()); + } + + public static class CreatesRandomSubFolders { + @Rule + public TemporaryFolder folder= new TemporaryFolder(); + + @Test + public void testUsingRandomTempFolders() throws IOException { + for (int i= 0; i < 20; i++) { + File newFolder= folder.newFolder(); + assertThat(Arrays.asList(createdFiles), not(hasItem(newFolder))); + createdFiles[i]= newFolder; + new File(newFolder, "a.txt").createNewFile(); + assertTrue(newFolder.exists()); + } + } + } + + @Test + public void randomSubFoldersAreDeleted() { + assertThat(testResult(CreatesRandomSubFolders.class), isSuccessful()); + for (File f : createdFiles) { + assertFalse(f.exists()); + } + } + + public static class CreatesRandomFiles { + @Rule + public TemporaryFolder folder= new TemporaryFolder(); + + @Test + public void testUsingRandomTempFiles() throws IOException { + for (int i= 0; i < 20; i++) { + File newFile= folder.newFile(); + assertThat(Arrays.asList(createdFiles), not(hasItem(newFile))); + createdFiles[i]= newFile; + assertTrue(newFile.exists()); + } + } + } + + @Test + public void randomFilesAreDeleted() { + assertThat(testResult(CreatesRandomFiles.class), isSuccessful()); + for (File f : createdFiles) { + assertFalse(f.exists()); + } + } + + @Test + public void recursiveDeleteFolderWithOneElement() throws IOException { + TemporaryFolder folder= new TemporaryFolder(); + folder.create(); + File file= folder.newFile("a"); + folder.delete(); + assertFalse(file.exists()); + assertFalse(folder.getRoot().exists()); + } + + @Test + public void recursiveDeleteFolderWithOneRandomElement() throws IOException { + TemporaryFolder folder= new TemporaryFolder(); + folder.create(); + File file= folder.newFile(); + folder.delete(); + assertFalse(file.exists()); + assertFalse(folder.getRoot().exists()); + } + + @Test + public void recursiveDeleteFolderWithZeroElements() throws IOException { + TemporaryFolder folder= new TemporaryFolder(); + folder.create(); + folder.delete(); + assertFalse(folder.getRoot().exists()); + } + + private static final String GET_ROOT_DUMMY= "dummy-getRoot"; + + private static final String NEW_FILE_DUMMY= "dummy-newFile"; + + private static final String NEW_FOLDER_DUMMY= "dummy-newFolder"; + + public static class IncorrectUsage { + public TemporaryFolder folder= new TemporaryFolder(); + + @Test + public void testGetRoot() throws IOException { + new File(folder.getRoot(), GET_ROOT_DUMMY).createNewFile(); + } + + @Test + public void testNewFile() throws IOException { + folder.newFile(NEW_FILE_DUMMY); + } + + @Test + public void testNewFolder() { + folder.newFolder(NEW_FOLDER_DUMMY); + } + } + + @Test + public void incorrectUsageWithoutApplyingTheRuleShouldNotPolluteTheCurrentWorkingDirectory() { + assertThat(testResult(IncorrectUsage.class), failureCountIs(3)); + assertFalse("getRoot should have failed early", new File(GET_ROOT_DUMMY).exists()); + assertFalse("newFile should have failed early", new File(NEW_FILE_DUMMY).exists()); + assertFalse("newFolder should have failed early", new File(NEW_FOLDER_DUMMY).exists()); + } + + @After + public void cleanCurrentWorkingDirectory() { + new File(GET_ROOT_DUMMY).delete(); + new File(NEW_FILE_DUMMY).delete(); + new File(NEW_FOLDER_DUMMY).delete(); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/TestRuleTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/TestRuleTest.java new file mode 100644 index 0000000..167eb20 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/TestRuleTest.java @@ -0,0 +1,289 @@ +package org.junit.tests.experimental.rules; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.hasSingleFailureContaining; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import static org.junit.matchers.JUnitMatchers.containsString; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +public class TestRuleTest { + private static boolean wasRun; + + public static class ExampleTest { + @Rule + public TestRule example= new TestRule() { + public Statement apply(final Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + wasRun= true; + base.evaluate(); + }; + }; + } + }; + + @Test + public void nothing() { + + } + } + + @Test + public void ruleIsIntroducedAndEvaluated() { + wasRun= false; + JUnitCore.runClasses(ExampleTest.class); + assertTrue(wasRun); + } + + @SuppressWarnings("deprecation") + public static class BothKindsOfRule implements TestRule, org.junit.rules.MethodRule { + public int applications = 0; + + public Statement apply(Statement base, FrameworkMethod method, + Object target) { + applications++; + return base; + } + + public Statement apply(Statement base, Description description) { + applications++; + return base; + } + } + + public static class OneFieldTwoKindsOfRule { + @Rule public BothKindsOfRule both = new BothKindsOfRule(); + + @Test public void onlyOnce() { + assertEquals(1, both.applications); + } + } + + + @Test + public void onlyApplyOnceEvenIfImplementsBothInterfaces() { + assertTrue(JUnitCore.runClasses(OneFieldTwoKindsOfRule.class).wasSuccessful()); + } + + public static class SonOfExampleTest extends ExampleTest { + + } + + @Test + public void ruleIsIntroducedAndEvaluatedOnSubclass() { + wasRun= false; + JUnitCore.runClasses(SonOfExampleTest.class); + assertTrue(wasRun); + } + + private static int runCount; + + public static class MultipleRuleTest { + private static class Increment implements TestRule { + public Statement apply(final Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + runCount++; + base.evaluate(); + }; + }; + } + } + + @Rule + public TestRule incrementor1= new Increment(); + + @Rule + public TestRule incrementor2= new Increment(); + + @Test + public void nothing() { + + } + } + + @Test + public void multipleRulesAreRun() { + runCount= 0; + JUnitCore.runClasses(MultipleRuleTest.class); + assertEquals(2, runCount); + } + + public static class NoRulesTest { + public int x; + + @Test + public void nothing() { + + } + } + + @Test + public void ignoreNonRules() { + Result result= JUnitCore.runClasses(NoRulesTest.class); + assertEquals(0, result.getFailureCount()); + } + + private static String log; + + public static class OnFailureTest { + @Rule + public TestRule watcher= new TestWatcher() { + @Override + protected void failed(Throwable e, Description description) { + log+= description + " " + e.getClass().getSimpleName(); + } + }; + + @Test + public void nothing() { + fail(); + } + } + + @Test + public void onFailure() { + log= ""; + Result result= JUnitCore.runClasses(OnFailureTest.class); + assertEquals(String.format("nothing(%s) AssertionError", OnFailureTest.class.getName()), log); + assertEquals(1, result.getFailureCount()); + } + + public static class WatchmanTest { + private static String watchedLog; + + @Rule + public TestRule watcher= new TestWatcher() { + @Override + protected void failed(Throwable e, Description description) { + watchedLog+= description + " " + + e.getClass().getSimpleName() + "\n"; + } + + @Override + protected void succeeded(Description description) { + watchedLog+= description + " " + "success!\n"; + } + }; + + @Test + public void fails() { + fail(); + } + + @Test + public void succeeds() { + } + } + + @Test + public void succeeded() { + WatchmanTest.watchedLog= ""; + JUnitCore.runClasses(WatchmanTest.class); + assertThat(WatchmanTest.watchedLog, containsString(String.format("fails(%s) AssertionError", WatchmanTest.class.getName()))); + assertThat(WatchmanTest.watchedLog, containsString(String.format("succeeds(%s) success!", WatchmanTest.class.getName()))); + } + + public static class BeforesAndAfters { + private static StringBuilder watchedLog= new StringBuilder(); + + @Before + public void before() { + watchedLog.append("before "); + } + + @Rule + public TestRule watcher= new LoggingTestWatcher(watchedLog); + + @After + public void after() { + watchedLog.append("after "); + } + + @Test + public void succeeds() { + watchedLog.append("test "); + } + } + + @Test + public void beforesAndAfters() { + BeforesAndAfters.watchedLog= new StringBuilder(); + JUnitCore.runClasses(BeforesAndAfters.class); + assertThat(BeforesAndAfters.watchedLog.toString(), + is("starting before test after succeeded finished ")); + } + + public static class WrongTypedField { + @Rule public int x = 5; + @Test public void foo() {} + } + + @Test public void validateWrongTypedField() { + assertThat(testResult(WrongTypedField.class), + hasSingleFailureContaining("must implement MethodRule")); + } + + public static class SonOfWrongTypedField extends WrongTypedField { + + } + + @Test public void validateWrongTypedFieldInSuperclass() { + assertThat(testResult(SonOfWrongTypedField.class), + hasSingleFailureContaining("must implement MethodRule")); + } + + public static class PrivateRule { + @SuppressWarnings("unused") + @Rule private TestRule rule = new TestName(); + @Test public void foo() {} + } + + @Test public void validatePrivateRule() { + assertThat(testResult(PrivateRule.class), + hasSingleFailureContaining("must be public")); + } + + public static class CustomTestName implements TestRule { + public String name = null; + + public Statement apply(final Statement base, final Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + name = description.getMethodName(); + base.evaluate(); + } + }; + } + } + + public static class UsesCustomMethodRule { + @Rule public CustomTestName counter = new CustomTestName(); + @Test public void foo() { + assertEquals("foo", counter.name); + } + } + + @Test public void useCustomMethodRule() { + assertThat(testResult(UsesCustomMethodRule.class), isSuccessful()); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/TestWatcherTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/TestWatcherTest.java new file mode 100644 index 0000000..19ac3e6 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/TestWatcherTest.java @@ -0,0 +1,52 @@ +package org.junit.tests.experimental.rules; + +import static junit.framework.Assert.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeTrue; +import static org.junit.runner.JUnitCore.runClasses; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; + +public class TestWatcherTest { + public static class ViolatedAssumptionTest { + private static StringBuilder watchedLog= new StringBuilder(); + + @Rule + public TestRule watcher= new LoggingTestWatcher(watchedLog); + + @Test + public void succeeds() { + assumeTrue(false); + } + } + + @Test + public void neitherLogSuccessNorFailedForViolatedAssumption() { + ViolatedAssumptionTest.watchedLog= new StringBuilder(); + runClasses(ViolatedAssumptionTest.class); + assertThat(ViolatedAssumptionTest.watchedLog.toString(), + is("starting finished ")); + } + + public static class FailingTest { + private static StringBuilder watchedLog= new StringBuilder(); + + @Rule + public TestRule watcher= new LoggingTestWatcher(watchedLog); + + @Test + public void succeeds() { + fail(); + } + } + + @Test + public void logFailingTest() { + FailingTest.watchedLog= new StringBuilder(); + runClasses(FailingTest.class); + assertThat(FailingTest.watchedLog.toString(), + is("starting failed finished ")); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/TestWatchmanTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/TestWatchmanTest.java new file mode 100644 index 0000000..9ef22cb --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/TestWatchmanTest.java @@ -0,0 +1,72 @@ +package org.junit.tests.experimental.rules; + +import static junit.framework.Assert.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeTrue; +import static org.junit.runner.JUnitCore.runClasses; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestWatchman; +import org.junit.runners.model.FrameworkMethod; + +@SuppressWarnings("deprecation") +public class TestWatchmanTest { + public static class ViolatedAssumptionTest { + @Rule + public static LoggingTestWatchman watchman= new LoggingTestWatchman(); + + @Test + public void succeeds() { + assumeTrue(false); + } + } + + @Test + public void neitherLogSuccessNorFailedForViolatedAssumption() { + runClasses(ViolatedAssumptionTest.class); + assertThat(ViolatedAssumptionTest.watchman.log.toString(), + is("starting finished ")); + } + + public static class FailingTest { + @Rule + public static LoggingTestWatchman watchman= new LoggingTestWatchman(); + + @Test + public void succeeds() { + fail(); + } + } + + @Test + public void logFailingTest() { + runClasses(FailingTest.class); + assertThat(FailingTest.watchman.log.toString(), + is("starting failed finished ")); + } + + private static class LoggingTestWatchman extends TestWatchman { + private final StringBuilder log= new StringBuilder(); + + @Override + public void succeeded(FrameworkMethod method) { + log.append("succeeded "); + } + + @Override + public void failed(Throwable e, FrameworkMethod method) { + log.append("failed "); + } + + @Override + public void starting(FrameworkMethod method) { + log.append("starting "); + } + + @Override + public void finished(FrameworkMethod method) { + log.append("finished "); + } + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/TimeoutRuleTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/TimeoutRuleTest.java new file mode 100644 index 0000000..28d3553 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/TimeoutRuleTest.java @@ -0,0 +1,39 @@ +package org.junit.tests.experimental.rules; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.matchers.JUnitMatchers.containsString; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.Timeout; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; + +public class TimeoutRuleTest { + public static class HasGlobalTimeout { + public static String log; + + @Rule public TestRule globalTimeout = new Timeout(20); + + @Test public void testInfiniteLoop1() { + log+= "ran1"; + for(;;) {} + } + + @Test public void testInfiniteLoop2() { + log+= "ran2"; + for(;;) {} + } + } + + @Ignore("For gump, for now") + @Test(timeout=100) public void globalTimeoutAvoidsInfiniteLoop() { + HasGlobalTimeout.log = ""; + Result result= JUnitCore.runClasses(HasGlobalTimeout.class); + assertEquals(2, result.getFailureCount()); + assertThat(HasGlobalTimeout.log, containsString("ran1")); + assertThat(HasGlobalTimeout.log, containsString("ran2")); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/rules/VerifierRuleTest.java b/junit4/src/test/java/org/junit/tests/experimental/rules/VerifierRuleTest.java new file mode 100644 index 0000000..ef75174 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/rules/VerifierRuleTest.java @@ -0,0 +1,133 @@ +package org.junit.tests.experimental.rules; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.hasFailureContaining; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; + +import java.util.concurrent.Callable; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.results.PrintableResult; +import org.junit.rules.ErrorCollector; +import org.junit.rules.Verifier; + +public class VerifierRuleTest { + public static class UsesErrorCollector { + @Rule + public ErrorCollector collector= new ErrorCollector(); + + @Test public void example() { + collector.addError(new Throwable("message")); + } + } + + @Test public void usedErrorCollectorShouldFail() { + assertThat(testResult(UsesErrorCollector.class), hasFailureContaining("message")); + } + + public static class UsesErrorCollectorTwice { + @Rule + public ErrorCollector collector= new ErrorCollector(); + + @Test public void example() { + collector.addError(new Throwable("first thing went wrong")); + collector.addError(new Throwable("second thing went wrong")); + } + } + + @Test public void usedErrorCollectorTwiceShouldFail() { + PrintableResult testResult= testResult(UsesErrorCollectorTwice.class); + assertThat(testResult, hasFailureContaining("first thing went wrong")); + assertThat(testResult, hasFailureContaining("second thing went wrong")); + } + + public static class UsesErrorCollectorCheckThat { + @Rule + public ErrorCollector collector= new ErrorCollector(); + + @Test public void example() { + collector.checkThat(3, is(4)); + collector.checkThat(5, is(6)); + collector.checkThat("reason 1", 7, is(8)); + collector.checkThat("reason 2", 9, is(16)); + } + } + + @Test public void usedErrorCollectorCheckThatShouldFail() { + PrintableResult testResult= testResult(UsesErrorCollectorCheckThat.class); + assertThat(testResult, hasFailureContaining("got: <3>")); + assertThat(testResult, hasFailureContaining("got: <5>")); + assertThat(testResult, hasFailureContaining("reason 1")); + assertThat(testResult, hasFailureContaining("got: <7>")); + assertThat(testResult, hasFailureContaining("reason 2")); + assertThat(testResult, hasFailureContaining("got: <9>")); + } + + public static class UsesErrorCollectorCheckSucceeds { + @Rule + public ErrorCollector collector= new ErrorCollector(); + + @Test public void example() { + collector.checkSucceeds(new Callable<Object>() { + public Object call() throws Exception { + throw new RuntimeException("first!"); + } + }); + collector.checkSucceeds(new Callable<Object>() { + public Object call() throws Exception { + throw new RuntimeException("second!"); + } + }); + } + } + + @Test public void usedErrorCollectorCheckSucceedsShouldFail() { + PrintableResult testResult= testResult(UsesErrorCollectorCheckSucceeds.class); + assertThat(testResult, hasFailureContaining("first!")); + assertThat(testResult, hasFailureContaining("second!")); + } + + public static class UsesErrorCollectorCheckSucceedsPasses { + @Rule + public ErrorCollector collector= new ErrorCollector(); + + @Test public void example() { + assertEquals(3, collector.checkSucceeds(new Callable<Object>() { + public Object call() throws Exception { + return 3; + } + })); + } + } + + @Test public void usedErrorCollectorCheckSucceedsShouldPass() { + PrintableResult testResult= testResult(UsesErrorCollectorCheckSucceedsPasses.class); + assertThat(testResult, isSuccessful()); + } + + private static String sequence; + + public static class UsesVerifier { + @Rule + public Verifier collector= new Verifier() { + @Override + protected void verify() { + sequence+= "verify "; + } + }; + + @Test public void example() { + sequence+= "test "; + } + } + + @Test public void verifierRunsAfterTest() { + sequence = ""; + assertThat(testResult(UsesVerifier.class), isSuccessful()); + assertEquals("test verify ", sequence); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/AllMembersSupplierTest.java b/junit4/src/test/java/org/junit/tests/experimental/theories/AllMembersSupplierTest.java new file mode 100644 index 0000000..e3fa0f0 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/AllMembersSupplierTest.java @@ -0,0 +1,34 @@ +package org.junit.tests.experimental.theories; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.List; + +import org.junit.Test; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.ParameterSignature; +import org.junit.experimental.theories.PotentialAssignment; +import org.junit.experimental.theories.internal.AllMembersSupplier; +import org.junit.runners.model.TestClass; + +public class AllMembersSupplierTest { + public static class HasDataPoints { + @DataPoints + public static Object[] objects= { 1, 2 }; + + public HasDataPoints(Object obj) { + } + } + + @Test + public void dataPointsAnnotationMeansTreatAsArrayOnly() + throws SecurityException, NoSuchMethodException { + List<PotentialAssignment> valueSources= new AllMembersSupplier( + new TestClass(HasDataPoints.class)) + .getValueSources(ParameterSignature.signatures( + HasDataPoints.class.getConstructor(Object.class)) + .get(0)); + assertThat(valueSources.size(), is(2)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/ParameterSignatureTest.java b/junit4/src/test/java/org/junit/tests/experimental/theories/ParameterSignatureTest.java new file mode 100644 index 0000000..c0191c4 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/ParameterSignatureTest.java @@ -0,0 +1,57 @@ +package org.junit.tests.experimental.theories; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeTrue; +import static org.junit.matchers.JUnitMatchers.hasItem; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.ParameterSignature; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.experimental.theories.suppliers.TestedOn; +import org.junit.runner.RunWith; + +@RunWith(Theories.class) +public class ParameterSignatureTest { + @DataPoint + public static Method getType() throws SecurityException, + NoSuchMethodException { + return ParameterSignatureTest.class.getMethod("getType", Method.class, + int.class); + } + + @DataPoint + public static int ZERO= 0; + + @DataPoint + public static int ONE= 1; + + @Theory + public void getType(Method method, int index) { + assumeTrue(index < method.getParameterTypes().length); + assertEquals(method.getParameterTypes()[index], ParameterSignature + .signatures(method).get(index).getType()); + } + + public void foo(@TestedOn(ints= { 1, 2, 3 }) + int x) { + } + + @Test + public void getAnnotations() throws SecurityException, + NoSuchMethodException { + Method method= ParameterSignatureTest.class.getMethod("foo", int.class); + List<Annotation> annotations= ParameterSignature.signatures(method) + .get(0).getAnnotations(); + assertThat(new ArrayList<Object>(annotations), + hasItem(is(TestedOn.class))); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/ParameterizedAssertionErrorTest.java b/junit4/src/test/java/org/junit/tests/experimental/theories/ParameterizedAssertionErrorTest.java new file mode 100644 index 0000000..91c0e04 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/ParameterizedAssertionErrorTest.java @@ -0,0 +1,75 @@ +package org.junit.tests.experimental.theories; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; +import static org.junit.internal.matchers.StringContains.containsString; +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.experimental.theories.internal.ParameterizedAssertionError; +import org.junit.runner.RunWith; + +@RunWith(Theories.class) +public class ParameterizedAssertionErrorTest { + @DataPoint + public static final String METHOD_NAME= "methodName"; + + @DataPoint + public static final NullPointerException NULL_POINTER_EXCEPTION= new NullPointerException(); + + @DataPoint + public static Object[] NO_OBJECTS= new Object[0]; + + @DataPoint + public static ParameterizedAssertionError A= new ParameterizedAssertionError( + NULL_POINTER_EXCEPTION, METHOD_NAME); + + @DataPoint + public static ParameterizedAssertionError B= new ParameterizedAssertionError( + NULL_POINTER_EXCEPTION, METHOD_NAME); + + @DataPoint + public static ParameterizedAssertionError B2= new ParameterizedAssertionError( + NULL_POINTER_EXCEPTION, "methodName2"); + + @Theory + public void equalParameterizedAssertionErrorsHaveSameToString( + ParameterizedAssertionError a, ParameterizedAssertionError b) { + assumeThat(a, is(b)); + assertThat(a.toString(), is(b.toString())); + } + + @Theory + public void differentParameterizedAssertionErrorsHaveDifferentToStrings( + ParameterizedAssertionError a, ParameterizedAssertionError b) { + assumeThat(a, not(b)); + assertThat(a.toString(), not(b.toString())); + } + + @Theory + public void equalsReturnsTrue(Throwable targetException, String methodName, + Object[] params) { + assertThat(new ParameterizedAssertionError(targetException, methodName, + params), is(new ParameterizedAssertionError(targetException, + methodName, params))); + } + + @Theory(nullsAccepted= false) + public void buildParameterizedAssertionError(String methodName, String param) { + assertThat(new ParameterizedAssertionError(new RuntimeException(), + methodName, param).toString(), containsString(methodName)); + } + + @Test + public void canJoinWhenToStringFails() { + assertThat(ParameterizedAssertionError.join(" ", new Object() { + @Override + public String toString() { + throw new UnsupportedOperationException(); + } + }), is("[toString failed]")); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Correspondent.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Correspondent.java new file mode 100644 index 0000000..60d7836 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Correspondent.java @@ -0,0 +1,7 @@ +package org.junit.tests.experimental.theories.extendingwithstubs; + +public interface Correspondent { + + String getAnswer(String question, String... bucket); + +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Guesser.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Guesser.java new file mode 100644 index 0000000..dcc5627 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Guesser.java @@ -0,0 +1,122 @@ +package org.junit.tests.experimental.theories.extendingwithstubs; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import java.util.Map.Entry; + +import org.hamcrest.BaseDescription; +import org.hamcrest.Description; +import org.junit.internal.AssumptionViolatedException; + +public class Guesser<T> extends ReguessableValue { + static class GuessMap extends HashMap<MethodCall, Object> implements + InvocationHandler { + private static final long serialVersionUID= 1L; + + public GuessMap(GuessMap guesses) { + super(guesses); + } + + public GuessMap() { + } + + GuessMap replaceGuess(Object oldValue, Object newValue) { + GuessMap newGuesses= new GuessMap(this); + for (Entry<MethodCall, Object> entry : newGuesses.entrySet()) { + if (entry.getValue().equals(oldValue)) + entry.setValue(newValue); + } + return newGuesses; + } + + protected Object generateGuess(Class<?> returnType) { + if (returnType.equals(String.class)) + return "GUESS" + new Random().nextInt(); + if (returnType.equals(Integer.class) + || returnType.equals(int.class)) + return new Random().nextInt(); + return null; + } + + Object getGuess(MethodCall call) { + if (!containsKey(call)) + put(call, generateGuess(call.getReturnType())); + return get(call); + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + return getGuess(new MethodCall(method, args)); + } + } + + private final GuessMap guesses; + + private final Class<? extends T> type; + + public Guesser(Class<? extends T> type) { + this(type, new GuessMap()); + } + + public Guesser(Class<? extends T> type2, GuessMap guesses) { + this.type= type2; + this.guesses= guesses; + } + + @SuppressWarnings("unchecked") + public T getProxy() { + return (T) Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[] { getType() }, guesses); + } + + @Override + public List<ReguessableValue> reguesses(AssumptionViolatedException e) { + final ArrayList<ReguessableValue> returnThis= new ArrayList<ReguessableValue>(); + e.describeTo(new BaseDescription() { + @Override + protected void append(char arg0) { + } + + boolean expectedSeen= false; + Object expected= null; + + @Override + public Description appendValue(Object value) { + noteValue(value); + return super.appendValue(value); + } + + private void noteValue(Object value) { + if (!expectedSeen) { + expected= value; + expectedSeen= true; + return; + } + + GuessMap newGuesses= guesses.replaceGuess(expected, value); + returnThis.add(new Guesser<T>(getType(), newGuesses)); + } + }); + return returnThis; + } + + @Override + public Object getValue() throws CouldNotGenerateValueException { + return getProxy(); + } + + public Class<? extends T> getType() { + return type; + } + + @Override + public String getDescription() throws CouldNotGenerateValueException { + return "guesser[" + type + "]"; + } + +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/GuesserQueue.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/GuesserQueue.java new file mode 100644 index 0000000..f3eb550 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/GuesserQueue.java @@ -0,0 +1,57 @@ +package org.junit.tests.experimental.theories.extendingwithstubs; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.experimental.theories.PotentialAssignment; +import org.junit.internal.AssumptionViolatedException; + +public class GuesserQueue extends ArrayList<ReguessableValue> { + static class ReguessableDecorator extends ReguessableValue { + private final PotentialAssignment delegate; + + public ReguessableDecorator(PotentialAssignment delegate) { + this.delegate= delegate; + } + + @Override + public List<ReguessableValue> reguesses(AssumptionViolatedException e) { + return Collections.emptyList(); + } + + @Override + public Object getValue() throws CouldNotGenerateValueException { + return delegate.getValue(); + } + + @Override + public String getDescription() throws CouldNotGenerateValueException { + return delegate.getDescription(); + } + } + + static GuesserQueue forSingleValues( + List<PotentialAssignment> potentials) { + GuesserQueue returnThis= new GuesserQueue(); + for (PotentialAssignment potentialParameterValue : potentials) { + returnThis + .add(new GuesserQueue.ReguessableDecorator(potentialParameterValue)); + } + return returnThis; + } + + private static final long serialVersionUID = 1L; + private ReguessableValue lastRemoved; + + public void update(AssumptionViolatedException e) { + if (lastRemoved != null) + addAll(lastRemoved.reguesses(e)); + } + + @Override + public ReguessableValue remove(int index) { + lastRemoved = super.remove(index); + return lastRemoved; + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/MethodCall.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/MethodCall.java new file mode 100644 index 0000000..85c44c5 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/MethodCall.java @@ -0,0 +1,54 @@ +package org.junit.tests.experimental.theories.extendingwithstubs; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MethodCall { + private final Method method; + private final Object[] args; + + public MethodCall(Method method, Object... args) { + this.method = method; + this.args = args; + } + + @Override + public boolean equals(Object obj) { + MethodCall call = (MethodCall) obj; + return call.method.equals(method) && Arrays.deepEquals(call.args, args); + } + + @Override + public int hashCode() { + return 1; + } + + public Class<?> getReturnType() { + return method.getReturnType(); + } + + @Override + public String toString() { + return String.format("%s(%s)", method.getName(), argListString()); + } + + private String argListString() { + if (args == null) + return null; + return argList().toString().substring(1, argList().toString().length() - 1); + } + + private List<Object> argList() { + ArrayList<Object> list = new ArrayList<Object>(); + for (Object arg : args) { + list.add(new StringableObject(arg)); + } + return list; + } + + public Object stringableObject(Object arg) { + return new StringableObject(arg).stringableObject(); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/ReguessableValue.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/ReguessableValue.java new file mode 100644 index 0000000..6c96ed6 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/ReguessableValue.java @@ -0,0 +1,16 @@ +package org.junit.tests.experimental.theories.extendingwithstubs; + +import java.util.List; + +import org.junit.experimental.theories.PotentialAssignment; +import org.junit.internal.AssumptionViolatedException; + +public abstract class ReguessableValue extends PotentialAssignment { + + public ReguessableValue() { + super(); + } + + public abstract List<ReguessableValue> reguesses( + AssumptionViolatedException e); +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StringableObject.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StringableObject.java new file mode 100644 index 0000000..94d5b31 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StringableObject.java @@ -0,0 +1,27 @@ +package org.junit.tests.experimental.theories.extendingwithstubs; + +import java.util.Arrays; + +public class StringableObject { + public Object obj; + + public StringableObject(Object obj) { + this.obj = obj; + } + + public Object stringableObject() { + if (isListableArray()) + return Arrays.asList((Object[]) obj); + else + return obj; + } + + private boolean isListableArray() { + Class<?> type = obj.getClass(); + return type.isArray() && !type.getComponentType().isPrimitive(); + } + + @Override public String toString() { + return stringableObject().toString(); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Stub.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Stub.java new file mode 100644 index 0000000..aa320d5 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/Stub.java @@ -0,0 +1,8 @@ +package org.junit.tests.experimental.theories.extendingwithstubs; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Stub { +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StubbedTheories.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StubbedTheories.java new file mode 100644 index 0000000..91c0b42 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StubbedTheories.java @@ -0,0 +1,65 @@ +package org.junit.tests.experimental.theories.extendingwithstubs; + + +import java.util.ArrayList; +import java.util.List; + +import org.junit.experimental.theories.ParameterSignature; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.internal.Assignments; +import org.junit.internal.AssumptionViolatedException; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +public class StubbedTheories extends Theories { + public StubbedTheories(Class<?> klass) throws InitializationError { + super(klass); + } + + @Override + public Statement methodBlock(FrameworkMethod method) { + return new StubbedTheoryAnchor(method, getTestClass()); + } + + public static class StubbedTheoryAnchor extends TheoryAnchor { + public StubbedTheoryAnchor(FrameworkMethod method, TestClass testClass) { + super(method, testClass); + } + + private List<GuesserQueue> queues= new ArrayList<GuesserQueue>(); + + @Override + protected void handleAssumptionViolation(AssumptionViolatedException e) { + super.handleAssumptionViolation(e); + for (GuesserQueue queue : queues) + queue.update(e); + } + + @Override + protected void runWithIncompleteAssignment(Assignments incomplete) + throws InstantiationException, IllegalAccessException, + Throwable { + GuesserQueue guessers= createGuesserQueue(incomplete); + queues.add(guessers); + while (!guessers.isEmpty()) + runWithAssignment(incomplete.assignNext(guessers.remove(0))); + queues.remove(guessers); + } + + private GuesserQueue createGuesserQueue(Assignments incomplete) + throws InstantiationException, IllegalAccessException { + ParameterSignature nextUnassigned= incomplete.nextUnassigned(); + + if (nextUnassigned.hasAnnotation(Stub.class)) { + GuesserQueue queue= new GuesserQueue(); + queue.add(new Guesser<Object>(nextUnassigned.getType())); + return queue; + } + + return GuesserQueue.forSingleValues(incomplete.potentialsForNextUnassigned()); + } + } + +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StubbedTheoriesTest.java b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StubbedTheoriesTest.java new file mode 100644 index 0000000..24abd7a --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/extendingwithstubs/StubbedTheoriesTest.java @@ -0,0 +1 @@ +package org.junit.tests.experimental.theories.extendingwithstubs;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assume.assumeThat;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
@RunWith(StubbedTheories.class)
public class StubbedTheoriesTest {
@Theory
public void ask(@Stub
Correspondent correspondent) {
assumeThat(correspondent.getAnswer("What is five?", "four", "five"),
is("five"));
}
}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/runner/SuccessfulWithDataPointFields.java b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/SuccessfulWithDataPointFields.java new file mode 100644 index 0000000..ba3f149 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/SuccessfulWithDataPointFields.java @@ -0,0 +1,204 @@ +package org.junit.tests.experimental.theories.runner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +@RunWith(Enclosed.class) +public class SuccessfulWithDataPointFields { + @RunWith(Theories.class) + public static class HasATwoParameterTheory { + @DataPoint + public static int ONE= 1; + + @Theory + public void allIntsAreEqual(int x, int y) { + assertThat(x, is(y)); + } + } + + @RunWith(Theories.class) + public static class BeforeAndAfterOnSameInstance { + @DataPoint + public static String A= "A"; + + private int befores= 0; + + @Before + public void incrementBefore() { + befores++; + } + + @Theory + public void stringsAreOK(String string) { + assertTrue(befores == 1); + } + } + + @RunWith(Theories.class) + public static class NewObjectEachTime { + @DataPoint + public static String A= "A"; + + @DataPoint + public static String B= "B"; + + private List<String> list= new ArrayList<String>(); + + @Theory + public void addToEmptyList(String string) { + list.add(string); + assertThat(list.size(), is(1)); + } + } + + @RunWith(Theories.class) + public static class PositiveInts { + @DataPoint + public static final int ONE= 1; + + private int x; + + public PositiveInts(int x) { + assumeTrue(x > 0); + this.x= x; + } + + @Theory + public void haveAPostiveSquare() { + assertTrue(x * x > 0); + } + } + + @RunWith(Theories.class) + public static class PositiveIntsWithNegativeField { + @DataPoint + public static final int ONE= 1; + @DataPoint + public static final int NEGONE= -1; + + private int x; + + public PositiveIntsWithNegativeField(int x) { + assumeTrue(x > 0); + this.x= x; + } + + @Theory + public void haveAPostiveSquare() { + assertTrue(x > 0); + } + } + + @RunWith(Theories.class) + public static class PositiveIntsWithMethodParams { + @DataPoint + public static final int ONE= 1; + + private int x; + + public PositiveIntsWithMethodParams(int x) { + assumeTrue(x > 0); + this.x= x; + } + + @Theory + public void haveAPostiveSquare(int y) { + assumeTrue(y > 0); + assertTrue(x * y > 0); + } + } + + @RunWith(Theories.class) + public static class DifferentTypesInConstructor { + @DataPoint + public static final int ONE= 1; + + @DataPoint public static final String A = "A"; + + public DifferentTypesInConstructor(int x) { + } + + @Theory + public void yesIndeed(String a) { + } + } + + @RunWith(Theories.class) + public static class BeforeAndAfterEachTime { + public static int befores= 0; + + @DataPoint + public static String A= "A"; + + @DataPoint + public static String B= "B"; + + @Before + public void incrementBefore() { + befores++; + } + + @BeforeClass public static void resetCalls() { + befores = 0; + } + + @Theory + public void stringsAreOK(String string) { + } + + @AfterClass public static void calledTwice() { + assertEquals(2, befores); + } + } + + @RunWith(Theories.class) + public static class OneTestTwoAnnotations { + public static int tests= 0; + + @DataPoint + public static String A= "A"; + + @BeforeClass public static void resetCalls() { + tests = 0; + } + + @Theory @Test + public void stringsAreOK(String string) { + tests++; + } + + @AfterClass public static void calledTwice() { + assertEquals(1, tests); + } + } + + @RunWith(Theories.class) + static public class StaticPublicNonDataPoints { + // DataPoint which passes the test + @DataPoint public static int ZERO = 0; + + // Not annotated as a DataPoint and therefore should be ignored: + public static int ONE = 1; + + @Theory + public void onlyAnnotatedFields(int i) { + assertTrue(i == 0); + } + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/runner/TheoriesPerformanceTest.java b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/TheoriesPerformanceTest.java new file mode 100644 index 0000000..52dc528 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/TheoriesPerformanceTest.java @@ -0,0 +1,37 @@ +package org.junit.tests.experimental.theories.runner; + +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeTrue; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import org.junit.Test; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +public class TheoriesPerformanceTest { + @RunWith(Theories.class) + public static class UpToTen { + @DataPoints + public static int[] ints= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + @Theory + public void threeInts(int x, int y, int z) { + // pass always + } + } + + private static final boolean TESTING_PERFORMANCE= false; + + // If we do not share the same instance of TestClass, repeatedly parsing the + // class's annotations looking for @Befores and @Afters gets really costly. + // + // Likewise, the TestClass must be passed into AllMembersSupplier, or the + // annotation parsing is again costly. + @Test + public void tryCombinationsQuickly() { + assumeTrue(TESTING_PERFORMANCE); + assertThat(testResult(UpToTen.class), isSuccessful()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/runner/UnsuccessfulWithDataPointFields.java b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/UnsuccessfulWithDataPointFields.java new file mode 100644 index 0000000..790d798 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/UnsuccessfulWithDataPointFields.java @@ -0,0 +1,126 @@ +package org.junit.tests.experimental.theories.runner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.failureCountIs; +import static org.junit.experimental.results.ResultMatchers.hasFailureContaining; +import static org.junit.experimental.results.ResultMatchers.hasSingleFailureContaining; +import static org.junit.matchers.JUnitMatchers.both; +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; +import org.junit.runners.model.TestClass; + +public class UnsuccessfulWithDataPointFields { + @RunWith(Theories.class) + public static class HasATheory { + @DataPoint + public static int ONE= 1; + + @Theory + public void everythingIsZero(int x) { + assertThat(x, is(0)); + } + } + + @Test + public void theoryClassMethodsShowUp() throws Exception { + assertThat(new Theories(HasATheory.class).getDescription() + .getChildren().size(), is(1)); + } + + @Test + public void theoryAnnotationsAreRetained() throws Exception { + assertThat(new TestClass(HasATheory.class).getAnnotatedMethods( + Theory.class).size(), is(1)); + } + + @Test + public void canRunTheories() throws Exception { + assertThat(testResult(HasATheory.class), + hasSingleFailureContaining("Expected")); + } + + @RunWith(Theories.class) + public static class DoesntUseParams { + @DataPoint + public static int ONE= 1; + + @Theory + public void everythingIsZero(int x, int y) { + assertThat(2, is(3)); + } + } + + @Test + public void reportBadParams() throws Exception { + assertThat(testResult(DoesntUseParams.class), + hasSingleFailureContaining("everythingIsZero(ONE, ONE)")); + } + + @RunWith(Theories.class) + public static class NullsOK { + @DataPoint + public static String NULL= null; + + @DataPoint + public static String A= "A"; + + @Theory + public void everythingIsA(String a) { + assertThat(a, is("A")); + } + } + + @Test + public void nullsUsedUnlessProhibited() throws Exception { + assertThat(testResult(NullsOK.class), + hasSingleFailureContaining("null")); + } + + @RunWith(Theories.class) + public static class DataPointsMustBeStatic { + @DataPoint + int THREE= 3; + + @DataPoint + int FOUR= 3; + + @Theory + public void numbers(int x) { + + } + } + + @Test + public void dataPointsMustBeStatic() { + assertThat( + testResult(DataPointsMustBeStatic.class), + both(failureCountIs(2)) + .and( + hasFailureContaining("DataPoint field THREE must be static")) + .and( + hasFailureContaining("DataPoint field FOUR must be static"))); + } + + @RunWith(Theories.class) + public static class TheoriesMustBePublic { + @DataPoint + public static int THREE= 3; + + @Theory + void numbers(int x) { + + } + } + + @Test + public void theoriesMustBePublic() { + assertThat( + testResult(TheoriesMustBePublic.class), + hasSingleFailureContaining("public")); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WhenNoParametersMatch.java b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WhenNoParametersMatch.java new file mode 100644 index 0000000..3c83d82 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WhenNoParametersMatch.java @@ -0,0 +1,50 @@ +package org.junit.tests.experimental.theories.runner; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.internal.matchers.StringContains.containsString; +import org.hamcrest.Matcher; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +@RunWith(Theories.class) +public class WhenNoParametersMatch { + @DataPoints + public static int[] ints= { 0, 1, 3, 5, 1776 }; + + @DataPoints + public static Matcher<?>[] matchers= { not(0), is(1) }; + + @RunWith(Theories.class) + public static class AssumptionsFail { + @DataPoint + public static int DATA= 0; + + @DataPoint + public static Matcher<Integer> MATCHER= null; + + @Theory + public void nonZeroIntsAreFun(int x) { + assumeThat(x, MATCHER); + } + } + + @Theory + public void showFailedAssumptionsWhenNoParametersFound(int data, + Matcher<Integer> matcher) throws Exception { + assumeThat(data, not(matcher)); + AssumptionsFail.DATA= data; + AssumptionsFail.MATCHER= matcher; + + String result= testResult(AssumptionsFail.class).toString(); + + assertThat(result, containsString(matcher.toString())); + assertThat(result, containsString("" + data)); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithDataPointMethod.java b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithDataPointMethod.java new file mode 100644 index 0000000..659f430 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithDataPointMethod.java @@ -0,0 +1,152 @@ +package org.junit.tests.experimental.theories.runner; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import static org.junit.internal.matchers.Each.each; +import static org.junit.internal.matchers.StringContains.containsString; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.PotentialAssignment; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.experimental.theories.internal.Assignments; +import org.junit.runner.JUnitCore; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; +import org.junit.runners.model.TestClass; + +public class WithDataPointMethod { + @RunWith(Theories.class) + public static class HasDataPointMethod { + @DataPoint + public static int oneHundred() { + return 100; + } + + @Theory + public void allIntsOk(int x) { + + } + } + + @RunWith(Theories.class) + public static class HasUglyDataPointMethod { + @DataPoint + public static int oneHundred() { + return 100; + } + + @DataPoint + public static int oneUglyHundred() { + throw new RuntimeException(); + } + + @Theory + public void allIntsOk(int x) { + + } + } + + @Test + public void pickUpDataPointMethods() { + assertThat(testResult(HasDataPointMethod.class), isSuccessful()); + } + + @Test + public void ignoreExceptionsFromDataPointMethods() { + assertThat(testResult(HasUglyDataPointMethod.class), isSuccessful()); + } + + @RunWith(Theories.class) + public static class DataPointMethodReturnsMutableObject { + @DataPoint + public static List<Object> empty() { + return new ArrayList<Object>(); + } + + @DataPoint + public static int ONE= 1; + + @DataPoint + public static int TWO= 2; + + @Theory + public void everythingsEmpty(List<Object> first, int number) { + assertThat(first.size(), is(0)); + first.add("a"); + } + } + + @Test + public void mutableObjectsAreCreatedAfresh() { + assertThat(failures(DataPointMethodReturnsMutableObject.class), empty()); + } + + @RunWith(Theories.class) + public static class HasDateMethod { + @DataPoint + public int oneHundred() { + return 100; + } + + public Date notADataPoint() { + return new Date(); + } + + @Theory + public void allIntsOk(int x) { + + } + + @Theory + public void onlyStringsOk(String s) { + + } + + @Theory + public void onlyDatesOk(Date d) { + + } + } + + @Test + public void ignoreDataPointMethodsWithWrongTypes() throws Exception { + assertThat(potentialValues( + HasDateMethod.class.getMethod("onlyStringsOk", String.class)) + .toString(), not(containsString("100"))); + } + + @Test + public void ignoreDataPointMethodsWithoutAnnotation() throws Throwable { + assertThat(potentialValues( + HasDateMethod.class.getMethod("onlyDatesOk", Date.class)) + .size(), is(0)); + } + + private List<PotentialAssignment> potentialValues(Method method) + throws Exception { + return Assignments.allUnassigned(method, + new TestClass(HasDateMethod.class)) + .potentialsForNextUnassigned(); + } + + private List<Failure> failures(Class<?> type) { + return JUnitCore.runClasses(type).getFailures(); + } + + private Matcher<Iterable<Failure>> empty() { + Matcher<Failure> nullValue= nullValue(); + return each(nullValue); + } +} diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithExtendedParameterSources.java b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithExtendedParameterSources.java new file mode 100644 index 0000000..9b5f0ae --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithExtendedParameterSources.java @@ -0,0 +1,146 @@ +package org.junit.tests.experimental.theories.runner; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import org.junit.Test; +import org.junit.experimental.results.ResultMatchers; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.experimental.theories.suppliers.TestedOn; +import org.junit.runner.JUnitCore; +import org.junit.runner.RunWith; + +public class WithExtendedParameterSources { + @RunWith(Theories.class) + public static class ParameterAnnotations { + @Theory + public void everythingIsOne(@TestedOn(ints= { 1 }) + int number) { + assertThat(number, is(1)); + } + } + + @Test + public void testedOnLimitsParameters() throws Exception { + assertThat(testResult(ParameterAnnotations.class), ResultMatchers + .isSuccessful()); + } + + @RunWith(Theories.class) + public static class ShouldFilterNull { + @DataPoint + public static String NULL= null; + + @DataPoint + public static String A= "a"; + + @Theory(nullsAccepted= false) + public void allStringsAreNonNull(String s) { + assertThat(s, notNullValue()); + } + } + + @Test + public void shouldFilterNull() { + assertThat(testResult(ShouldFilterNull.class), isSuccessful()); + } + + @RunWith(Theories.class) + public static class DataPointArrays { + public static String log= ""; + + @DataPoints + public static String[] STRINGS= new String[] { "A", "B" }; + + @Theory + public void addToLog(String string) { + log+= string; + } + } + + @Test + public void getDataPointsFromArray() { + DataPointArrays.log= ""; + JUnitCore.runClasses(DataPointArrays.class); + assertThat(DataPointArrays.log, is("AB")); + } + + @RunWith(Theories.class) + public static class DataPointArrayMethod { + public static String log= ""; + + @DataPoints + public static String[] STRINGS() { + return new String[] { "A", "B" }; + }; + + @Theory + public void addToLog(String string) { + log+= string; + } + } + + @Test + public void getDataPointsFromArrayMethod() { + DataPointArrayMethod.log= ""; + JUnitCore.runClasses(DataPointArrayMethod.class); + assertThat(DataPointArrayMethod.log, is("AB")); + } + + @RunWith(Theories.class) + public static class DataPointMalformedArrayMethods { + public static String log= ""; + + @DataPoints + public static String[] STRINGS() { + return new String[] { "A", "B" }; + }; + + @DataPoints + public static String STRING() { + return "C"; + } + + @DataPoints + public static int[] INTS() { + return new int[] { 1, 2, 3 }; + } + + @Theory + public void addToLog(String string) { + log+= string; + } + } + + @Test + public void getDataPointsFromArrayMethodInSpiteOfMalformedness() { + DataPointArrayMethod.log= ""; + JUnitCore.runClasses(DataPointArrayMethod.class); + assertThat(DataPointArrayMethod.log, is("AB")); + } + + @RunWith(Theories.class) + public static class DataPointArrayToBeUsedForWholeParameter { + public static String log= ""; + + @DataPoint + public static String[] STRINGS= new String[] { "A", "B" }; + + @Theory + public void addToLog(String[] strings) { + log+= strings[0]; + } + } + + @Test + public void dataPointCanBeArray() { + DataPointArrayToBeUsedForWholeParameter.log= ""; + JUnitCore.runClasses(DataPointArrayToBeUsedForWholeParameter.class); + assertThat(DataPointArrayToBeUsedForWholeParameter.log, is("A")); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithOnlyTestAnnotations.java b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithOnlyTestAnnotations.java new file mode 100644 index 0000000..2204fb6 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithOnlyTestAnnotations.java @@ -0,0 +1,80 @@ +package org.junit.tests.experimental.theories.runner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.failureCountIs; +import static org.junit.experimental.results.ResultMatchers.isSuccessful; +import static org.junit.matchers.JUnitMatchers.containsString; +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; + +public class WithOnlyTestAnnotations { + @RunWith(Theories.class) + public static class HonorExpectedException { + @Test(expected= NullPointerException.class) + public void shouldThrow() { + + } + } + + @Test + public void honorExpected() throws Exception { + assertThat(testResult(HonorExpectedException.class).failureCount(), is(1)); + } + + @RunWith(Theories.class) + public static class HonorExpectedExceptionPasses { + @Test(expected= NullPointerException.class) + public void shouldThrow() { + throw new NullPointerException(); + } + } + + @Test + public void honorExpectedPassing() throws Exception { + assertThat(testResult(HonorExpectedExceptionPasses.class), isSuccessful()); + } + + @RunWith(Theories.class) + public static class HonorTimeout { + @Test(timeout= 5) + public void shouldStop() { + while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + + } + } + } + } + + @Test + public void honorTimeout() throws Exception { + assertThat(testResult(HonorTimeout.class), failureCountIs(1)); + } + + @RunWith(Theories.class) + static public class ErrorWhenTestHasParametersDespiteTheories { + @DataPoint + public static int ZERO = 0; + + @Test + public void testMethod(int i) { + } + } + + @Test public void testErrorWhenTestHasParametersDespiteTheories() { + JUnitCore core = new JUnitCore(); + Result result = core.run(ErrorWhenTestHasParametersDespiteTheories.class); + assertEquals(1, result.getFailureCount()); + String message = result.getFailures().get(0).getMessage(); + assertThat(message, containsString("should have no parameters")); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithUnresolvedGenericTypeVariablesOnTheoryParms.java b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithUnresolvedGenericTypeVariablesOnTheoryParms.java new file mode 100644 index 0000000..579fc7e --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/experimental/theories/runner/WithUnresolvedGenericTypeVariablesOnTheoryParms.java @@ -0,0 +1,177 @@ +package org.junit.tests.experimental.theories.runner; + +import static org.junit.Assert.*; +import static org.junit.experimental.results.PrintableResult.*; +import static org.junit.experimental.results.ResultMatchers.*; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.junit.experimental.results.PrintableResult; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +public class WithUnresolvedGenericTypeVariablesOnTheoryParms { + @Test + public void whereTypeVariableIsOnTheTheory() { + PrintableResult result= testResult(TypeVariableOnTheoryOnly.class); + assertThat(result, isSuccessful()); + } + + @RunWith(Theories.class) + public static class TypeVariableOnTheoryOnly { + @DataPoint + public static List<String> strings = Arrays.asList("foo", "bar"); + + @Theory + public <T> void forItems(Collection<?> items) { + } + } + + @Test + public void whereTypeVariableIsOnTheoryParm() { + PrintableResult result= testResult(TypeVariableOnTheoryParm.class); + assertThat(result, hasSingleFailureContaining("unresolved type variable T")); + } + + @RunWith(Theories.class) + public static class TypeVariableOnTheoryParm { + @DataPoint + public static String string = "foo"; + + @Theory + public <T> void forItem(T item) { + } + } + + @Test + public void whereTypeVariableIsOnParameterizedTheoryParm() { + PrintableResult result= testResult(TypeVariableOnParameterizedTheoryParm.class); + assertThat(result, hasSingleFailureContaining("unresolved type variable T")); + } + + @RunWith(Theories.class) + public static class TypeVariableOnParameterizedTheoryParm { + @DataPoint + public static List<String> strings = Arrays.asList("foo", "bar"); + + @Theory + public <T> void forItems(Collection<T> items) { + } + } + + @Test + public void whereTypeVariableIsOnWildcardUpperBoundOnTheoryParm() { + PrintableResult result= testResult(TypeVariableOnWildcardUpperBoundOnTheoryParm.class); + assertThat(result, hasSingleFailureContaining("unresolved type variable U")); + } + + @RunWith(Theories.class) + public static class TypeVariableOnWildcardUpperBoundOnTheoryParm { + @DataPoint + public static List<String> strings = Arrays.asList("foo", "bar"); + + @Theory + public <U> void forItems(Collection<? extends U> items) { + } + } + + @Test + public void whereTypeVariableIsOnWildcardLowerBoundOnTheoryParm() { + PrintableResult result= testResult(TypeVariableOnWildcardLowerBoundOnTheoryParm.class); + assertThat(result, hasSingleFailureContaining("unresolved type variable V")); + } + + @RunWith(Theories.class) + public static class TypeVariableOnWildcardLowerBoundOnTheoryParm { + @DataPoint + public static List<String> strings = Arrays.asList("foo", "bar"); + + @Theory + public <V> void forItems(Collection<? super V> items) { + } + } + + @Test + public void whereTypeVariableIsOnArrayTypeOnTheoryParm() { + PrintableResult result= testResult(TypeVariableOnArrayTypeOnTheoryParm.class); + assertThat(result, hasSingleFailureContaining("unresolved type variable T")); + } + + @RunWith(Theories.class) + public static class TypeVariableOnArrayTypeOnTheoryParm { + @DataPoints + public static String[][] items() { + return new String[][] { new String[] { "foo" }, new String[] { "bar" } }; + } + + @Theory + public <T> void forItems(T[] items) { + } + } + + @Test + public void whereTypeVariableIsOnComponentOfArrayTypeOnTheoryParm() { + PrintableResult result= testResult(TypeVariableOnComponentOfArrayTypeOnTheoryParm.class); + assertThat(result, hasSingleFailureContaining("unresolved type variable U")); + } + + @RunWith(Theories.class) + public static class TypeVariableOnComponentOfArrayTypeOnTheoryParm { + @DataPoints + public static List<?>[][] items() { + return new List<?>[][] { + new List<?>[] { Arrays.asList("foo") }, + new List<?>[] { Arrays.asList("bar") } + }; + } + + @Theory + public <U> void forItems(Collection<U>[] items) { + } + } + + @Test + public void whereTypeVariableIsOnTheoryClass() { + PrintableResult result= testResult(TypeVariableOnTheoryClass.class); + assertThat(result, hasSingleFailureContaining("unresolved type variable T")); + } + + @RunWith(Theories.class) + public static class TypeVariableOnTheoryClass<T> { + @DataPoint + public static String item = "bar"; + + @Theory + public void forItem(T item) { + } + } + + @Test + public void whereTypeVariablesAbound() { + PrintableResult result= testResult(TypeVariablesAbound.class); + assertThat(result, failureCountIs(7)); + assertThat(result, hasFailureContaining("unresolved type variable A")); + assertThat(result, hasFailureContaining("unresolved type variable B")); + assertThat(result, hasFailureContaining("unresolved type variable C")); + assertThat(result, hasFailureContaining("unresolved type variable D")); + assertThat(result, hasFailureContaining("unresolved type variable E")); + assertThat(result, hasFailureContaining("unresolved type variable F")); + assertThat(result, hasFailureContaining("unresolved type variable G")); + } + + @RunWith(Theories.class) + public static class TypeVariablesAbound<A, B extends A, C extends Collection<B>> { + @Theory + public <D, E extends D, F, G> void forItem(A first, Collection<B> second, + Map<C, ? extends D> third, List<? super E> fourth, F[] fifth, + Collection<G>[] sixth) { + } + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/internal/runners/statements/FailOnTimeoutTest.java b/junit4/src/test/java/org/junit/tests/internal/runners/statements/FailOnTimeoutTest.java new file mode 100644 index 0000000..152d641 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/internal/runners/statements/FailOnTimeoutTest.java @@ -0,0 +1,110 @@ +package org.junit.tests.internal.runners.statements; + +import static java.lang.Thread.sleep; +import static org.junit.Assert.assertTrue; +import static org.hamcrest.core.Is.is; +import org.junit.Rule; +import org.junit.Test; +import org.junit.internal.runners.statements.FailOnTimeout; +import org.junit.rules.ExpectedException; +import org.junit.runners.model.Statement; + +/** + * @author Asaf Ary, Stefan Birkner + */ +public class FailOnTimeoutTest { + private static final int TIMEOUT= 100; + + @Rule + public final ExpectedException thrown= ExpectedException.none(); + + private final TestStatement statement= new TestStatement(); + + private final FailOnTimeout failOnTimeout= new FailOnTimeout(statement, + TIMEOUT); + + @Test + public void throwExceptionWithNiceMessageOnTimeout() throws Throwable { + thrown.expectMessage("test timed out after 100 milliseconds"); + evaluateWithWaitDuration(TIMEOUT + 50); + } + + @Test + public void sendUpExceptionThrownByStatement() throws Throwable { + RuntimeException exception= new RuntimeException(); + thrown.expect(is(exception)); + evaluateWithException(exception); + } + + @Test + public void throwExceptionIfTheSecondCallToEvaluateNeedsTooMuchTime() + throws Throwable { + thrown.expect(Exception.class); + evaluateWithWaitDuration(0); + evaluateWithWaitDuration(TIMEOUT + 50); + } + + @Test + public void throwTimeoutExceptionOnSecondCallAlthoughFirstCallThrowsException() + throws Throwable { + thrown.expectMessage("test timed out after 100 milliseconds"); + try { + evaluateWithException(new RuntimeException()); + } catch (Throwable expected) { + } + evaluateWithWaitDuration(TIMEOUT + 50); + } + + private void evaluateWithException(Exception exception) throws Throwable { + statement.nextException= exception; + statement.waitDuration= 0; + failOnTimeout.evaluate(); + } + + private void evaluateWithWaitDuration(int waitDuration) throws Throwable { + statement.nextException= null; + statement.waitDuration= waitDuration; + failOnTimeout.evaluate(); + } + + private static final class TestStatement extends Statement { + int waitDuration; + + Exception nextException; + + @Override + public void evaluate() throws Throwable { + sleep(waitDuration); + if (nextException != null) + throw nextException; + } + } + + @Test + public void stopEndlessStatement() throws Throwable { + InfiniteLoopStatement infiniteLoop= new InfiniteLoopStatement(); + FailOnTimeout infiniteLoopTimeout= new FailOnTimeout(infiniteLoop, + TIMEOUT); + try { + infiniteLoopTimeout.evaluate(); + } catch (Exception timeoutException) { + sleep(20); // time to interrupt the thread + int firstCount= InfiniteLoopStatement.COUNT; + sleep(20); // time to increment the count + assertTrue("Thread has not been stopped.", + firstCount == InfiniteLoopStatement.COUNT); + } + } + + private static final class InfiniteLoopStatement extends Statement { + private static int COUNT= 0; + + @Override + public void evaluate() throws Throwable { + while (true) { + sleep(10); //sleep in order to enable interrupting thread + ++COUNT; + } + } + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/AllTestsTest.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/AllTestsTest.java new file mode 100644 index 0000000..d9076e3 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/AllTestsTest.java @@ -0,0 +1,81 @@ +package org.junit.tests.junit3compatibility; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.matchers.JUnitMatchers.containsString; +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.junit.runner.JUnitCore; +import org.junit.runner.RunWith; +import org.junit.runners.AllTests; + +public class AllTestsTest { + + private static boolean run; + + public static class OneTest extends TestCase { + public void testSomething() { + run= true; + } + } + + @RunWith(AllTests.class) + public static class All { + static public junit.framework.Test suite() { + TestSuite suite= new TestSuite(); + suite.addTestSuite(OneTest.class); + return suite; + } + } + + @org.junit.Test public void ensureTestIsRun() { + JUnitCore runner= new JUnitCore(); + run= false; // Have to explicitly set run here because the runner might independently run OneTest above + runner.run(All.class); + assertTrue(run); + } + + @org.junit.Test public void correctTestCount() throws Throwable { + AllTests tests= new AllTests(All.class); + assertEquals(1, tests.testCount()); + } + + @org.junit.Test public void someUsefulDescription() throws Throwable { + AllTests tests= new AllTests(All.class); + assertThat(tests.getDescription().toString(), containsString("OneTest")); + } + + public static class JUnit4Test { + @org.junit.Test public void testSomething() { + run= true; + } + } + + @RunWith(AllTests.class) + public static class AllJUnit4 { + static public junit.framework.Test suite() { + TestSuite suite= new TestSuite(); + suite.addTest(new JUnit4TestAdapter(JUnit4Test.class)); + return suite; + } + } + + @org.junit.Test public void correctTestCountAdapted() throws Throwable { + AllTests tests= new AllTests(AllJUnit4.class); + assertEquals(1, tests.testCount()); + } + + @RunWith(AllTests.class) + public static class BadSuiteMethod { + public static junit.framework.Test suite() { + throw new RuntimeException("can't construct"); + } + } + + @org.junit.Test(expected= RuntimeException.class) + public void exceptionThrownWhenSuiteIsBad() throws Throwable { + new AllTests(BadSuiteMethod.class); + } +} diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/ClassRequestTest.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/ClassRequestTest.java new file mode 100644 index 0000000..91bc037 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/ClassRequestTest.java @@ -0,0 +1,19 @@ +package org.junit.tests.junit3compatibility; + +import static org.junit.Assert.assertNull; +import org.junit.Test; +import org.junit.internal.builders.SuiteMethodBuilder; + +public class ClassRequestTest { + public static class PrivateSuiteMethod { + static junit.framework.Test suite() { + return null; + } + } + + @Test + public void noSuiteMethodIfMethodPrivate() throws Throwable { + assertNull(new SuiteMethodBuilder() + .runnerForClass(PrivateSuiteMethod.class)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/ForwardCompatibilityPrintingTest.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/ForwardCompatibilityPrintingTest.java new file mode 100644 index 0000000..34a08a6 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/ForwardCompatibilityPrintingTest.java @@ -0,0 +1,89 @@ +package org.junit.tests.junit3compatibility; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import junit.textui.ResultPrinter; +import junit.textui.TestRunner; +import org.junit.Assert; +import org.junit.Test; + +public class ForwardCompatibilityPrintingTest extends TestCase { + static class TestResultPrinter extends ResultPrinter { + TestResultPrinter(PrintStream writer) { + super(writer); + } + + /* + * Spoof printing time so the tests are deterministic + */ + @Override + protected String elapsedTimeAsString(long runTime) { + return "0"; + } + } + + public void testError() { + ByteArrayOutputStream output= new ByteArrayOutputStream(); + TestRunner runner= new TestRunner(new TestResultPrinter( + new PrintStream(output))); + + String expected= expected(new String[] { ".E", "Time: 0", + "Errors here", "", "FAILURES!!!", + "Tests run: 1, Failures: 0, Errors: 1", "" }); + ResultPrinter printer= new TestResultPrinter(new PrintStream(output)) { + @Override + public void printErrors(TestResult result) { + getWriter().println("Errors here"); + } + }; + runner.setPrinter(printer); + TestSuite suite= new TestSuite(); + suite.addTest(new TestCase() { + @Override + public void runTest() throws Exception { + throw new Exception(); + } + }); + runner.doRun(suite); + assertEquals(expected, output.toString()); + } + + public static class ATest { + @Test public void error() { + Assert.fail(); + } + } + + public void testErrorAdapted() { + ByteArrayOutputStream output= new ByteArrayOutputStream(); + TestRunner runner= new TestRunner(new TestResultPrinter( + new PrintStream(output))); + + String expected= expected(new String[] { ".E", "Time: 0", + "Errors here", "", "FAILURES!!!", + "Tests run: 1, Failures: 0, Errors: 1", "" }); + ResultPrinter printer= new TestResultPrinter(new PrintStream(output)) { + @Override + public void printErrors(TestResult result) { + getWriter().println("Errors here"); + } + }; + runner.setPrinter(printer); + runner.doRun(new JUnit4TestAdapter(ATest.class)); + assertEquals(expected, output.toString()); + } + + private String expected(String[] lines) { + OutputStream expected= new ByteArrayOutputStream(); + PrintStream expectedWriter= new PrintStream(expected); + for (int i= 0; i < lines.length; i++) + expectedWriter.println(lines[i]); + return expected.toString(); + } +} diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/ForwardCompatibilityTest.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/ForwardCompatibilityTest.java new file mode 100644 index 0000000..2c73089 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/ForwardCompatibilityTest.java @@ -0,0 +1,222 @@ +package org.junit.tests.junit3compatibility; + +import junit.framework.AssertionFailedError; +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestCase; +import junit.framework.TestFailure; +import junit.framework.TestListener; +import junit.framework.TestResult; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; + +public class ForwardCompatibilityTest extends TestCase { + static String fLog; + + static public class NewTest { + @Before public void before() { + fLog+= "before "; + } + @After public void after() { + fLog+= "after "; + } + @Test public void test() { + fLog+= "test "; + } + } + + public void testCompatibility() { + fLog= ""; + TestResult result= new TestResult(); + junit.framework.Test adapter= new JUnit4TestAdapter(NewTest.class); + adapter.run(result); + assertEquals("before test after ", fLog); + } + + public void testToString() { + JUnit4TestAdapter adapter= new JUnit4TestAdapter(NewTest.class); + junit.framework.Test test= adapter.getTests().get(0); + assertEquals(String.format("test(%s)", NewTest.class.getName()), test.toString()); + } + + public void testUseGlobalCache() { + JUnit4TestAdapter adapter1= new JUnit4TestAdapter(NewTest.class); + JUnit4TestAdapter adapter2= new JUnit4TestAdapter(NewTest.class); + assertSame(adapter1.getTests().get(0), adapter2.getTests().get(0)); + } + + static Exception exception= new Exception(); + + public static class ErrorTest { + @Test public void error() throws Exception { + throw exception; + } + } + public void testException() { + TestResult result= new TestResult(); + junit.framework.Test adapter= new JUnit4TestAdapter(ErrorTest.class); + adapter.run(result); + assertEquals(exception, result.errors().nextElement().thrownException()); + } + + public void testNotifyResult() { + JUnit4TestAdapter adapter= new JUnit4TestAdapter(ErrorTest.class); + TestResult result= new TestResult(); + final StringBuffer log= new StringBuffer(); + result.addListener(new TestListener() { + + public void startTest(junit.framework.Test test) { + log.append(" start " + test); + } + + public void endTest(junit.framework.Test test) { + log.append(" end " + test); + } + + public void addFailure(junit.framework.Test test, AssertionFailedError t) { + log.append(" failure " + test); + } + + public void addError(junit.framework.Test test, Throwable t) { + log.append(" error " + test); + } + }); + adapter.run(result); + String testName= String.format("error(%s)", ErrorTest.class.getName()); + assertEquals(String.format(" start %s error %s end %s", testName, testName, testName), log.toString()); + } + + + public static class NoExceptionTest { + @Test(expected=Exception.class) public void succeed() throws Exception { + } + } + public void testNoException() { + TestResult result= new TestResult(); + junit.framework.Test adapter= new JUnit4TestAdapter(NoExceptionTest.class); + adapter.run(result); + assertFalse(result.wasSuccessful()); + } + + public static class ExpectedTest { + @Test(expected= Exception.class) public void expected() throws Exception { + throw new Exception(); + } + } + public void testExpected() { + TestResult result= new TestResult(); + junit.framework.Test adapter= new JUnit4TestAdapter(ExpectedTest.class); + adapter.run(result); + assertTrue(result.wasSuccessful()); + } + + public static class UnExpectedExceptionTest { + @Test(expected= Exception.class) public void expected() throws Exception { + throw new Error(); + } + } + + static String log; + public static class BeforeClassTest { + @BeforeClass public static void beforeClass() { + log+= "before class "; + } + @Before public void before() { + log+= "before "; + } + @Test public void one() { + log+= "test "; + } + @Test public void two() { + log+= "test "; + } + @After public void after() { + log+= "after "; + } + @AfterClass public static void afterClass() { + log+= "after class "; + } + } + + public void testBeforeAndAfterClass() { + log= ""; + TestResult result= new TestResult(); + junit.framework.Test adapter= new JUnit4TestAdapter(BeforeClassTest.class); + adapter.run(result); + assertEquals("before class before test after before test after after class ", log); + } + + public static class ExceptionInBeforeTest { + @Before public void error() { + throw new Error(); + } + @Test public void nothing() { + } + } + + public void testExceptionInBefore() { + TestResult result= new TestResult(); + junit.framework.Test adapter= new JUnit4TestAdapter(ExceptionInBeforeTest.class); + adapter.run(result); + assertEquals(1, result.errorCount()); + } + + public static class InvalidMethodTest { + @BeforeClass public void shouldBeStatic() {} + @Test public void aTest() {} + } + + public void testInvalidMethod() { + TestResult result= new TestResult(); + junit.framework.Test adapter= new JUnit4TestAdapter(InvalidMethodTest.class); + adapter.run(result); + assertEquals(1, result.errorCount()); + TestFailure failure= result.errors().nextElement(); + assertTrue(failure.exceptionMessage().contains("Method shouldBeStatic() should be static")); + } + + private static boolean wasRun= false; + + public static class MarkerRunner extends Runner { + public MarkerRunner(Class<?> klass) { + } + + @Override + public void run(RunNotifier notifier) { + wasRun= true; + } + + @Override + public int testCount() { + return 0; + } + + @Override + public Description getDescription() { + return Description.EMPTY; + } + } + + @RunWith(MarkerRunner.class) + public static class NoTests { + } + + public void testRunWithClass() { + wasRun= false; + TestResult result= new TestResult(); + junit.framework.Test adapter= new JUnit4TestAdapter(NoTests.class); + adapter.run(result); + assertTrue(wasRun); + } + + public void testToStringSuite() { + junit.framework.Test adapter= new JUnit4TestAdapter(NoTests.class); + assertEquals(NoTests.class.getName(), adapter.toString()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/InitializationErrorForwardCompatibilityTest.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/InitializationErrorForwardCompatibilityTest.java new file mode 100644 index 0000000..6c87a8b --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/InitializationErrorForwardCompatibilityTest.java @@ -0,0 +1,100 @@ +package org.junit.tests.junit3compatibility; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import junit.framework.AssertionFailedError; +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestListener; +import junit.framework.TestResult; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; + +public class InitializationErrorForwardCompatibilityTest { + public static class CantInitialize extends Runner { + private static final String UNIQUE_ERROR_MESSAGE= "Unique error message"; + + public CantInitialize(Class<?> klass) throws Exception { + throw new Exception(UNIQUE_ERROR_MESSAGE); + } + + @Override + public Description getDescription() { + return Description.EMPTY; + } + + @Override + public void run(RunNotifier notifier) { + } + } + + @RunWith(CantInitialize.class) + public static class CantInitializeTests { + } + + private JUnit4TestAdapter fAdapter; + + @Before public void createAdapter() { + fAdapter= new JUnit4TestAdapter( + CantInitializeTests.class); + } + + @Test + public void initializationErrorsShowUpAsWarnings() { + assertEquals(1, fAdapter.getTests().size()); + } + + @Test + public void initializationErrorsAreThrownAtRuntime() { + TestResult result= new TestResult(); + fAdapter.run(result); + assertEquals(1, result.errorCount()); + assertEquals(CantInitialize.UNIQUE_ERROR_MESSAGE, result.errors() + .nextElement().exceptionMessage()); + } + + private final class ErrorRememberingListener implements TestListener { + private junit.framework.Test fError; + + public void addError(junit.framework.Test test, Throwable t) { + fError= test; + } + + public void addFailure(junit.framework.Test test, + AssertionFailedError t) { + } + + public void endTest(junit.framework.Test test) { + } + + public void startTest(junit.framework.Test test) { + } + + public junit.framework.Test getError() { + return fError; + } + } + + @Test + public void generatedErrorTestsMatchUp() { + junit.framework.Test shouldFail= fAdapter.getTests().get(0); + TestResult result= new TestResult(); + ErrorRememberingListener listener= new ErrorRememberingListener(); + result.addListener(listener); + fAdapter.run(result); + assertNotNull(listener.getError()); + assertTrue(shouldFail == listener.getError()); + } + + public static class InitializesWithError extends BlockJUnit4ClassRunner { + public InitializesWithError(Class<?> klass) throws Exception { + super(klass); + throw new Exception(); + } + } +} diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/JUnit38ClassRunnerTest.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/JUnit38ClassRunnerTest.java new file mode 100644 index 0000000..52fbd2c --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/JUnit38ClassRunnerTest.java @@ -0,0 +1,79 @@ +package org.junit.tests.junit3compatibility; + +import static org.junit.Assert.assertEquals; +import junit.extensions.TestDecorator; +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.junit.Assert; +import org.junit.Test; +import org.junit.internal.runners.JUnit38ClassRunner; +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; + +public class JUnit38ClassRunnerTest { + public static class MyTest extends TestCase { + public void testA() { + + } + } + + @Test public void plansDecoratorCorrectly() { + JUnit38ClassRunner runner= new JUnit38ClassRunner(new TestDecorator(new TestSuite(MyTest.class))); + assertEquals(1, runner.testCount()); + } + + public static class AnnotatedTest { + @Test public void foo() { + Assert.fail(); + } + } + + @Test public void canUnadaptAnAdapter() { + JUnit38ClassRunner runner= new JUnit38ClassRunner(new JUnit4TestAdapter(AnnotatedTest.class)); + Result result= new JUnitCore().run(runner); + Failure failure= result.getFailures().get(0); + assertEquals(Description.createTestDescription(AnnotatedTest.class, "foo"), failure.getDescription()); + } + + static int count; + + static public class OneTest extends TestCase { + public void testOne() { + } + } + + @Test public void testListener() throws Exception { + JUnitCore runner= new JUnitCore(); + RunListener listener= new RunListener() { + @Override + public void testStarted(Description description) { + assertEquals(Description.createTestDescription(OneTest.class, "testOne"), + description); + count++; + } + }; + + runner.addListener(listener); + count= 0; + Result result= runner.run(OneTest.class); + assertEquals(1, count); + assertEquals(1, result.getRunCount()); + } + + public static class ClassWithInvalidMethod extends TestCase { + @SuppressWarnings("unused") + private void testInvalid() {} + } + + @Test public void invalidTestMethodReportedCorrectly() { + Result result= JUnitCore.runClasses(ClassWithInvalidMethod.class); + Failure failure= result.getFailures().get(0); + assertEquals("warning", failure.getDescription().getMethodName()); + assertEquals("junit.framework.TestSuite$1", failure.getDescription().getClassName()); + } + +} diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/OldTestClassAdaptingListenerTest.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/OldTestClassAdaptingListenerTest.java new file mode 100644 index 0000000..8c68a1a --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/OldTestClassAdaptingListenerTest.java @@ -0,0 +1,27 @@ +package org.junit.tests.junit3compatibility; + +import static org.junit.Assert.assertEquals; +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +import junit.framework.TestListener; +import org.junit.Test; +import org.junit.internal.runners.JUnit38ClassRunner; +import org.junit.runner.Result; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; + +public class OldTestClassAdaptingListenerTest { + @Test + public void addFailureDelegatesToNotifier() { + Result result= new Result(); + RunListener listener= result.createListener(); + RunNotifier notifier= new RunNotifier(); + notifier.addFirstListener(listener); + TestCase testCase= new TestCase() { + }; + TestListener adaptingListener= new JUnit38ClassRunner(testCase) + .createAdaptingListener(notifier); + adaptingListener.addFailure(testCase, new AssertionFailedError()); + assertEquals(1, result.getFailureCount()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/OldTests.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/OldTests.java new file mode 100644 index 0000000..ab154a6 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/OldTests.java @@ -0,0 +1,11 @@ +package org.junit.tests.junit3compatibility; +import junit.framework.Test; +import org.junit.runner.RunWith; +import org.junit.runners.AllTests; + +@RunWith(AllTests.class) +public class OldTests { + static public Test suite() { + return junit.tests.AllTests.suite(); + } +} diff --git a/junit4/src/test/java/org/junit/tests/junit3compatibility/SuiteMethodTest.java b/junit4/src/test/java/org/junit/tests/junit3compatibility/SuiteMethodTest.java new file mode 100644 index 0000000..c4890d0 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/junit3compatibility/SuiteMethodTest.java @@ -0,0 +1,123 @@ +package org.junit.tests.junit3compatibility; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; + +public class SuiteMethodTest { + public static boolean wasRun; + + static public class OldTest extends TestCase { + public OldTest(String name) { + super(name); + } + + public static junit.framework.Test suite() { + TestSuite result= new TestSuite(); + result.addTest(new OldTest("notObviouslyATest")); + return result; + } + + public void notObviouslyATest() { + wasRun= true; + } + } + + @Test public void makeSureSuiteIsCalled() { + wasRun= false; + JUnitCore.runClasses(OldTest.class); + assertTrue(wasRun); + } + + static public class NewTest { + @Test public void sample() { + wasRun= true; + } + + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(NewTest.class); + } + } + + @Test public void makeSureSuiteWorksWithJUnit4Classes() { + wasRun= false; + JUnitCore.runClasses(NewTest.class); + assertTrue(wasRun); + } + + + public static class CompatibilityTest { + @Ignore @Test + public void ignored() { + } + + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(CompatibilityTest.class); + } + } + + // when executing as JUnit 3, ignored tests are stripped out before execution + @Test + public void descriptionAndRunNotificationsAreConsistent() { + Result result= JUnitCore.runClasses(CompatibilityTest.class); + assertEquals(0, result.getIgnoreCount()); + + Description description= Request.aClass(CompatibilityTest.class).getRunner().getDescription(); + assertEquals(0, description.getChildren().size()); + } + + static public class NewTestSuiteFails { + @Test public void sample() { + wasRun= true; + } + + public static junit.framework.Test suite() { + fail("called with JUnit 4 runner"); + return null; + } + } + + @Test public void suiteIsUsedWithJUnit4Classes() { + wasRun= false; + Result result= JUnitCore.runClasses(NewTestSuiteFails.class); + assertEquals(1, result.getFailureCount()); + assertFalse(wasRun); + } + + static public class NewTestSuiteNotUsed { + private static boolean wasIgnoredRun; + + @Test public void sample() { + wasRun= true; + } + + @Ignore @Test public void ignore() { + wasIgnoredRun= true; + } + + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(NewTestSuiteNotUsed.class); + } + } + + @Test public void makeSureSuiteNotUsedWithJUnit4Classes2() { + wasRun= false; + NewTestSuiteNotUsed.wasIgnoredRun= false; + Result res= JUnitCore.runClasses(NewTestSuiteNotUsed.class); + assertTrue(wasRun); + assertFalse(NewTestSuiteNotUsed.wasIgnoredRun); + assertEquals(0, res.getFailureCount()); + assertEquals(1, res.getRunCount()); + assertEquals(0, res.getIgnoreCount()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/listening/ListenerTest.java b/junit4/src/test/java/org/junit/tests/listening/ListenerTest.java new file mode 100644 index 0000000..964c4d4 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/listening/ListenerTest.java @@ -0,0 +1,33 @@ +package org.junit.tests.listening; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.notification.RunListener; + +public class ListenerTest { + static private String log; + public static class OneTest { + @Test public void nothing() { + } + } + @Test public void notifyListenersInTheOrderInWhichTheyAreAdded() { + JUnitCore core= new JUnitCore(); + log= ""; + core.addListener(new RunListener() { + @Override + public void testRunStarted(Description description) throws Exception { + log+="first "; + } + }); + core.addListener(new RunListener() { + @Override + public void testRunStarted(Description description) throws Exception { + log+="second "; + } + }); + core.run(OneTest.class); + assertEquals("first second ", log); + } +} diff --git a/junit4/src/test/java/org/junit/tests/listening/RunnerTest.java b/junit4/src/test/java/org/junit/tests/listening/RunnerTest.java new file mode 100644 index 0000000..a07848d --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/listening/RunnerTest.java @@ -0,0 +1,69 @@ +package org.junit.tests.listening; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.notification.RunListener; + +public class RunnerTest { + + private boolean wasRun; + + public class MyListener extends RunListener { + + int testCount; + + @Override + public void testRunStarted(Description description) { + this.testCount= description.testCount(); + } + } + + public static class Example { + @Test public void empty() { + } + } + + @Test public void newTestCount() { + JUnitCore runner= new JUnitCore(); + MyListener listener= new MyListener(); + runner.addListener(listener); + runner.run(Example.class); + assertEquals(1, listener.testCount); + } + + public static class ExampleTest extends TestCase { + public void testEmpty() { + } + } + + @Test public void oldTestCount() { + JUnitCore runner= new JUnitCore(); + MyListener listener= new MyListener(); + runner.addListener(listener); + runner.run(ExampleTest.class); + assertEquals(1, listener.testCount); + } + + public static class NewExample { + @Test public void empty() { + } + } + + @Test public void testFinished() { + JUnitCore runner= new JUnitCore(); + wasRun= false; + RunListener listener= new MyListener() { + @Override + public void testFinished(Description description) { + wasRun= true; + } + }; + runner.addListener(listener); + runner.run(NewExample.class); + assertTrue(wasRun); + } +} diff --git a/junit4/src/test/java/org/junit/tests/listening/TestListenerTest.java b/junit4/src/test/java/org/junit/tests/listening/TestListenerTest.java new file mode 100644 index 0000000..35c0c75 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/listening/TestListenerTest.java @@ -0,0 +1,63 @@ +package org.junit.tests.listening; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import org.junit.Test; +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; + +public class TestListenerTest { + + int count; + + class ErrorListener extends RunListener { + @Override + public void testRunStarted(Description description) throws Exception { + throw new Error(); + } + } + + public static class OneTest { + @Test public void nothing() {} + } + + @Test(expected=Error.class) public void failingListener() { + JUnitCore runner= new JUnitCore(); + runner.addListener(new ErrorListener()); + runner.run(OneTest.class); + } + + class ExceptionListener extends ErrorListener { + @Override + public void testRunStarted(Description description) throws Exception { + count++; + throw new Exception(); + } + } + + @Test public void removeFailingListeners() { + JUnitCore core= new JUnitCore(); + core.addListener(new ExceptionListener()); + + count= 0; + Result result= core.run(OneTest.class); + assertEquals(1, count); + assertEquals(1, result.getFailureCount()); + Failure testFailure= result.getFailures().get(0); + assertEquals(Description.TEST_MECHANISM, testFailure.getDescription()); + + count= 0; + core.run(OneTest.class); + assertEquals(0, count); // Doesn't change because listener was removed + } + + @Test public void freshResultEachTime() { + JUnitCore core= new JUnitCore(); + Result first= core.run(OneTest.class); + Result second= core.run(OneTest.class); + assertNotSame(first, second); + } +} diff --git a/junit4/src/test/java/org/junit/tests/listening/TextListenerTest.java b/junit4/src/test/java/org/junit/tests/listening/TextListenerTest.java new file mode 100644 index 0000000..b9684a6 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/listening/TextListenerTest.java @@ -0,0 +1,65 @@ +package org.junit.tests.listening; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +import junit.framework.TestCase; +import org.junit.Test; +import org.junit.internal.TextListener; +import org.junit.runner.JUnitCore; +import org.junit.tests.TestSystem; + +public class TextListenerTest extends TestCase { + + private JUnitCore runner; + private OutputStream results; + private TextListener listener; + + @Override + public void setUp() { + runner= new JUnitCore(); + TestSystem system= new TestSystem(); + results= system.outContents(); + listener= new TextListener(system); + runner.addListener(listener); + } + + public static class OneTest { + @Test public void one() {} + } + + public void testSuccess() throws Exception { + runner.run(OneTest.class); + assertTrue(results.toString().startsWith(convert(".\nTime: "))); + assertTrue(results.toString().endsWith(convert("\n\nOK (1 test)\n\n"))); + } + + public static class ErrorTest { + @Test public void error() throws Exception {throw new Exception();} + } + + public void testError() throws Exception { + runner.run(ErrorTest.class); + assertTrue(results.toString().startsWith(convert(".E\nTime: "))); + assertTrue(results.toString().indexOf(convert("\nThere was 1 failure:\n1) error(org.junit.tests.listening.TextListenerTest$ErrorTest)\njava.lang.Exception")) != -1); + } + + public static class Slow { + @Test public void pause() throws InterruptedException { + Thread.sleep(1000); + } + } + + public void testTime() { + runner.run(Slow.class); + assertFalse(results.toString().contains("Time: 0")); + } + + private String convert(String string) { + OutputStream resultsStream= new ByteArrayOutputStream(); + PrintStream writer= new PrintStream(resultsStream); + writer.println(); + return string.replace("\n", resultsStream.toString()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/listening/UserStopTest.java b/junit4/src/test/java/org/junit/tests/listening/UserStopTest.java new file mode 100644 index 0000000..822e203 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/listening/UserStopTest.java @@ -0,0 +1,28 @@ +package org.junit.tests.listening; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.Request; +import org.junit.runner.notification.RunNotifier; +import org.junit.runner.notification.StoppedByUserException; + +public class UserStopTest { + private RunNotifier fNotifier; + + @Before public void createNotifier() { + fNotifier= new RunNotifier(); + fNotifier.pleaseStop(); + } + + @Test(expected=StoppedByUserException.class) public void userStop() { + fNotifier.fireTestStarted(null); + } + + public static class OneTest { + @Test public void foo() {} + } + + @Test(expected=StoppedByUserException.class) public void stopClassRunner() throws Exception { + Request.aClass(OneTest.class).getRunner().run(fNotifier); + } +} diff --git a/junit4/src/test/java/org/junit/tests/manipulation/FilterTest.java b/junit4/src/test/java/org/junit/tests/manipulation/FilterTest.java new file mode 100644 index 0000000..242e990 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/manipulation/FilterTest.java @@ -0,0 +1,49 @@ +package org.junit.tests.manipulation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.manipulation.Filter; + +public class FilterTest { + public static class NamedFilter extends Filter { + private final String fName; + + public NamedFilter(String name) { + fName= name; + } + + @Override + public boolean shouldRun(Description description) { + return false; + } + + @Override + public String describe() { + return fName; + } + } + + @Test + public void intersectionText() { + NamedFilter a= new NamedFilter("a"); + NamedFilter b= new NamedFilter("b"); + assertEquals("a and b", a.intersect(b).describe()); + assertEquals("b and a", b.intersect(a).describe()); + } + + @Test + public void intersectSelf() { + NamedFilter a= new NamedFilter("a"); + assertSame(a, a.intersect(a)); + } + + @Test + public void intersectAll() { + NamedFilter a= new NamedFilter("a"); + assertSame(a, a.intersect(Filter.ALL)); + assertSame(a, Filter.ALL.intersect(a)); + assertSame(Filter.ALL, Filter.ALL.intersect(Filter.ALL)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/manipulation/FilterableTest.java b/junit4/src/test/java/org/junit/tests/manipulation/FilterableTest.java new file mode 100644 index 0000000..3d35169 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/manipulation/FilterableTest.java @@ -0,0 +1,62 @@ +package org.junit.tests.manipulation; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.manipulation.Filter; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +public class FilterableTest { + public static class FilteredRunner extends Parameterized { + public FilteredRunner(Class<?> klass) throws Throwable { + super(klass); + filter(new Filter() { + + @Override + public boolean shouldRun(Description description) { + return !description.getDisplayName().contains("skip"); + } + + @Override + public String describe() { + return "skip methods containing the word 'skip'"; + } + }); + } + } + + @RunWith(FilteredRunner.class) + public static class FilteredTest { + @Parameters + public static List<Object[]> parameters() { + return Arrays.asList(new Object[] { 3 }, new Object[] { 4 }); + } + + public FilteredTest(int x) { + } + + @Test + public void skipThis() { + Assert.fail(); + } + + @Test + public void runThis() { + } + } + + @Test + public void testFilterInRunnerConstructor() { + Result result= JUnitCore.runClasses(FilteredTest.class); + assertTrue(result.wasSuccessful()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/manipulation/SingleMethodTest.java b/junit4/src/test/java/org/junit/tests/manipulation/SingleMethodTest.java new file mode 100644 index 0000000..393a844 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/manipulation/SingleMethodTest.java @@ -0,0 +1,168 @@ +package org.junit.tests.manipulation; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.Arrays; +import java.util.List; + +import junit.framework.JUnit4TestAdapter; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runners.Parameterized; +import org.junit.runners.Suite; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Suite.SuiteClasses; + +public class SingleMethodTest { + public static int count; + + static public class OneTimeSetup { + @BeforeClass public static void once() { + count++; + } + + @Test public void one() { + } + + @Test public void two() { + } + } + + @Test public void oneTimeSetup() throws Exception { + count = 0; + Runner runner = Request.method(OneTimeSetup.class, "one").getRunner(); + Result result = new JUnitCore().run(runner); + + assertEquals(1, count); + assertEquals(1, result.getRunCount()); + } + + @RunWith(Parameterized.class) + static public class ParameterizedOneTimeSetup { + @Parameters + public static List<Object[]> params() { + return Arrays.asList(new Object[] {1}, new Object[] {2}); + } + + public ParameterizedOneTimeSetup(int x) { + } + + @Test public void one() { + } + } + + @Test public void parameterizedFilterToSingleMethod() throws Exception { + count = 0; + Runner runner = Request.method(ParameterizedOneTimeSetup.class, + "one[0]").getRunner(); + Result result = new JUnitCore().run(runner); + + assertEquals(1, result.getRunCount()); + } + + @RunWith(Parameterized.class) + static public class ParameterizedOneTimeBeforeClass { + @Parameters + public static List<Object[]> params() { + return Arrays.asList(new Object[] {1}, new Object[] {2}); + } + + public ParameterizedOneTimeBeforeClass(int x) { + } + + @BeforeClass public static void once() { + count++; + } + + @Test public void one() { + } + } + + + @Test public void parameterizedBeforeClass() throws Exception { + count = 0; + JUnitCore.runClasses(ParameterizedOneTimeBeforeClass.class); + assertEquals(1, count); + } + + @Test public void filteringAffectsPlan() throws Exception { + Runner runner = Request.method(OneTimeSetup.class, "one").getRunner(); + assertEquals(1, runner.testCount()); + } + + @Test public void nonexistentMethodCreatesFailure() throws Exception { + assertEquals(1, new JUnitCore().run( + Request.method(OneTimeSetup.class, "thisMethodDontExist")) + .getFailureCount()); + } + + @Test(expected = NoTestsRemainException.class) + public void filteringAwayEverythingThrowsException() throws NoTestsRemainException { + Filterable runner = (Filterable) Request.aClass(OneTimeSetup.class).getRunner(); + runner.filter(new Filter() { + @Override + public boolean shouldRun(Description description) { + return false; + } + + @Override + public String describe() { + return null; + } + }); + } + + public static class TestOne { + @Test public void a() { + } + + @Test public void b() { + } + } + + public static class TestTwo { + @Test public void a() { + } + + @Test public void b() { + } + } + + @RunWith(Suite.class) + @SuiteClasses( { TestOne.class, TestTwo.class }) + public static class OneTwoSuite { + } + + @Test public void eliminateUnnecessaryTreeBranches() throws Exception { + Runner runner = Request.aClass(OneTwoSuite.class).filterWith( + Description.createTestDescription(TestOne.class, "a")) + .getRunner(); + Description description = runner.getDescription(); + assertEquals(1, description.getChildren().size()); + } + + public static class HasSuiteMethod { + @Test public void a() {} + @Test public void b() {} + + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(HasSuiteMethod.class); + } + } + + @Test public void classesWithSuiteMethodsAreFiltered() { + int testCount= Request.method(HasSuiteMethod.class, "a").getRunner().getDescription().testCount(); + assertThat(testCount, is(1)); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/manipulation/SortableTest.java b/junit4/src/test/java/org/junit/tests/manipulation/SortableTest.java new file mode 100644 index 0000000..eac7aa8 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/manipulation/SortableTest.java @@ -0,0 +1,149 @@ +package org.junit.tests.manipulation; + +import static org.junit.Assert.assertEquals; + +import java.util.Comparator; + +import junit.framework.JUnit4TestAdapter; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; + +@RunWith(Enclosed.class) +public class SortableTest { + private static Comparator<Description> forward() { + return new Comparator<Description>() { + public int compare(Description o1, Description o2) { + return o1.getDisplayName().compareTo(o2.getDisplayName()); + } + }; + } + + private static Comparator<Description> backward() { + return new Comparator<Description>() { + public int compare(Description o1, Description o2) { + return o2.getDisplayName().compareTo(o1.getDisplayName()); + } + }; + } + + public static class TestClassRunnerIsSortable { + private static String log= ""; + + public static class SortMe { + @Test public void a() { log+= "a"; } + @Test public void b() { log+= "b"; } + @Test public void c() { log+= "c"; } + } + + @Before public void resetLog() { + log= ""; + } + + @Test public void sortingForwardWorksOnTestClassRunner() { + Request forward= Request.aClass(SortMe.class).sortWith(forward()); + + new JUnitCore().run(forward); + assertEquals("abc", log); + } + + @Test public void sortingBackwardWorksOnTestClassRunner() { + Request backward= Request.aClass(SortMe.class).sortWith(backward()); + + new JUnitCore().run(backward); + assertEquals("cba", log); + } + + @RunWith(Enclosed.class) + public static class Enclosing { + public static class A { + @Test public void a() { log+= "Aa"; } + @Test public void b() { log+= "Ab"; } + @Test public void c() { log+= "Ac"; } + } + public static class B { + @Test public void a() { log+= "Ba"; } + @Test public void b() { log+= "Bb"; } + @Test public void c() { log+= "Bc"; } + } + } + + @Test public void sortingForwardWorksOnSuite() { + Request forward= Request.aClass(Enclosing.class).sortWith(forward()); + + new JUnitCore().run(forward); + assertEquals("AaAbAcBaBbBc", log); + } + + @Test public void sortingBackwardWorksOnSuite() { + Request backward= Request.aClass(Enclosing.class).sortWith(backward()); + + new JUnitCore().run(backward); + assertEquals("BcBbBaAcAbAa", log); + } + } + + public static class TestClassRunnerIsSortableWithSuiteMethod { + private static String log= ""; + + public static class SortMe { + @Test public void a() { log+= "a"; } + @Test public void b() { log+= "b"; } + @Test public void c() { log+= "c"; } + + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(SortMe.class); + } + } + + @Before public void resetLog() { + log= ""; + } + + @Test public void sortingForwardWorksOnTestClassRunner() { + Request forward= Request.aClass(SortMe.class).sortWith(forward()); + + new JUnitCore().run(forward); + assertEquals("abc", log); + } + + @Test public void sortingBackwardWorksOnTestClassRunner() { + Request backward= Request.aClass(SortMe.class).sortWith(backward()); + + new JUnitCore().run(backward); + assertEquals("cba", log); + } + } + + public static class UnsortableRunnersAreHandledWithoutCrashing { + public static class UnsortableRunner extends Runner { + public UnsortableRunner(Class<?> klass) { + } + + @Override + public Description getDescription() { + return Description.EMPTY; + } + + @Override + public void run(RunNotifier notifier) { + } + } + + @RunWith(UnsortableRunner.class) + public static class Unsortable { + @Test public void a() {} + } + + @Test public void unsortablesAreHandledWithoutCrashing() { + Request unsorted= Request.aClass(Unsortable.class).sortWith(forward()); + new JUnitCore().run(unsorted); + } + } +} diff --git a/junit4/src/test/java/org/junit/tests/package-info.java b/junit4/src/test/java/org/junit/tests/package-info.java new file mode 100644 index 0000000..21d611c --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/package-info.java @@ -0,0 +1,8 @@ +/** + * Tests the JUnit functionality. + * + * Corresponds to {@link junit.tests.AllTests} in Junit 3.x. + * + * @since 4.0 + */ +package org.junit.tests;
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/running/classes/BlockJUnit4ClassRunnerTest.java b/junit4/src/test/java/org/junit/tests/running/classes/BlockJUnit4ClassRunnerTest.java new file mode 100644 index 0000000..a19cd94 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/BlockJUnit4ClassRunnerTest.java @@ -0,0 +1,33 @@ +package org.junit.tests.running.classes; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; + +public class BlockJUnit4ClassRunnerTest { + public static class OuterClass { + public class Enclosed { + @Test + public void test() { + } + } + } + + @Test + public void detectNonStaticEnclosedClass() throws Exception { + try { + new BlockJUnit4ClassRunner(OuterClass.Enclosed.class); + } catch (InitializationError e) { + List<Throwable> causes= e.getCauses(); + assertEquals("Wrong number of causes.", 1, causes.size()); + assertEquals( + "Wrong exception.", + "The inner class org.junit.tests.running.classes.BlockJUnit4ClassRunnerTest$OuterClass$Enclosed is not static.", + causes.get(0).getMessage()); + } + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/running/classes/EnclosedTest.java b/junit4/src/test/java/org/junit/tests/running/classes/EnclosedTest.java new file mode 100644 index 0000000..8a6973a --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/EnclosedTest.java @@ -0,0 +1,40 @@ +package org.junit.tests.running.classes; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; + +public class EnclosedTest { + @RunWith(Enclosed.class) + public static class Enclosing { + public static class A { + @Test public void a() {} + @Test public void b() {} + } + public static class B { + @Test public void a() {} + @Test public void b() {} + @Test public void c() {} + } + } + + @Test public void enclosedRunnerPlansEnclosedClasses() throws Exception { + Runner runner= Request.aClass(Enclosing.class).getRunner(); + assertEquals(5, runner.testCount()); + } + + @Test public void enclosedRunnerRunsEnclosedClasses() throws Exception { + Result result= JUnitCore.runClasses(Enclosing.class); + assertEquals(5, result.getRunCount()); + } + + @Test public void enclosedRunnerIsNamedForEnclosingClass() throws Exception { + assertEquals(Enclosing.class.getName(), Request.aClass(Enclosing.class) + .getRunner().getDescription().getDisplayName()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/classes/IgnoreClassTest.java b/junit4/src/test/java/org/junit/tests/running/classes/IgnoreClassTest.java new file mode 100644 index 0000000..e193712 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/IgnoreClassTest.java @@ -0,0 +1,26 @@ +package org.junit.tests.running.classes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; + +public class IgnoreClassTest { + @Ignore("For a good reason") public static class IgnoreMe { + @Test public void iFail() { + fail(); + } + + @Test public void iFailToo() { + fail(); + } + } + + @Test public void ignoreClass() { + Result result= JUnitCore.runClasses(IgnoreMe.class); + assertEquals(0, result.getFailureCount()); + assertEquals(1, result.getIgnoreCount()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/classes/ParameterizedTestTest.java b/junit4/src/test/java/org/junit/tests/running/classes/ParameterizedTestTest.java new file mode 100644 index 0000000..d48433f --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/ParameterizedTestTest.java @@ -0,0 +1,216 @@ +package org.junit.tests.running.classes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.matchers.JUnitMatchers.containsString; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.model.InitializationError; + +public class ParameterizedTestTest { + @RunWith(Parameterized.class) + static public class FibonacciTest { + @Parameters + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, + { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } }); + } + + private int fInput; + + private int fExpected; + + public FibonacciTest(int input, int expected) { + fInput= input; + fExpected= expected; + } + + @Test + public void test() { + assertEquals(fExpected, fib(fInput)); + } + + private int fib(int x) { + return 0; + } + } + + @Test + public void count() { + Result result= JUnitCore.runClasses(FibonacciTest.class); + assertEquals(7, result.getRunCount()); + assertEquals(6, result.getFailureCount()); + } + + @Test + public void failuresNamedCorrectly() { + Result result= JUnitCore.runClasses(FibonacciTest.class); + assertEquals(String + .format("test[1](%s)", FibonacciTest.class.getName()), result + .getFailures().get(0).getTestHeader()); + } + + @Test + public void countBeforeRun() throws Exception { + Runner runner= Request.aClass(FibonacciTest.class).getRunner(); + assertEquals(7, runner.testCount()); + } + + @Test + public void plansNamedCorrectly() throws Exception { + Runner runner= Request.aClass(FibonacciTest.class).getRunner(); + Description description= runner.getDescription(); + assertEquals("[0]", description.getChildren().get(0).getDisplayName()); + } + + private static String fLog; + + @RunWith(Parameterized.class) + static public class BeforeAndAfter { + @BeforeClass + public static void before() { + fLog+= "before "; + } + + @AfterClass + public static void after() { + fLog+= "after "; + } + + public BeforeAndAfter(int x) { + + } + + @Parameters + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { { 3 } }); + } + + @Test + public void aTest() { + } + } + + @Test + public void beforeAndAfterClassAreRun() { + fLog= ""; + JUnitCore.runClasses(BeforeAndAfter.class); + assertEquals("before after ", fLog); + } + + @RunWith(Parameterized.class) + static public class EmptyTest { + @BeforeClass + public static void before() { + fLog+= "before "; + } + + @AfterClass + public static void after() { + fLog+= "after "; + } + } + + @Test + public void validateClassCatchesNoParameters() { + Result result= JUnitCore.runClasses(EmptyTest.class); + assertEquals(1, result.getFailureCount()); + } + + @RunWith(Parameterized.class) + static public class IncorrectTest { + @Test + public int test() { + return 0; + } + + @Parameters + public static Collection<Object[]> data() { + return Collections.singletonList(new Object[] {1}); + } + } + + @Test + public void failuresAddedForBadTestMethod() throws Exception { + Result result= JUnitCore.runClasses(IncorrectTest.class); + assertEquals(1, result.getFailureCount()); + } + + @RunWith(Parameterized.class) + static public class ProtectedParametersTest { + @Parameters + protected static Collection<Object[]> data() { + return Collections.emptyList(); + } + + @Test + public void aTest() { + } + } + + @Test + public void meaningfulFailureWhenParametersNotPublic() throws Exception { + Result result= JUnitCore.runClasses(ProtectedParametersTest.class); + String expected= String.format( + "No public static parameters method on class %s", + ProtectedParametersTest.class.getName()); + assertEquals(expected, result.getFailures().get(0).getMessage()); + } + + @RunWith(Parameterized.class) + static public class WrongElementType { + @Parameters + public static Collection<String> data() { + return Arrays.asList("a", "b", "c"); + } + + @Test + public void aTest() { + } + } + + @Test + public void meaningfulFailureWhenParameterListsAreNotArrays() { + String expected= String.format( + "%s.data() must return a Collection of arrays.", + WrongElementType.class.getName()); + assertThat(testResult(WrongElementType.class).toString(), + containsString(expected)); + } + + @RunWith(Parameterized.class) + static public class PrivateConstructor { + private PrivateConstructor(int x) { + + } + + @Parameters + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { { 3 } }); + } + + @Test + public void aTest() { + } + } + + @Test(expected=InitializationError.class) + public void exceptionWhenPrivateConstructor() throws Throwable { + new Parameterized(PrivateConstructor.class); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/classes/ParentRunnerFilteringTest.java b/junit4/src/test/java/org/junit/tests/running/classes/ParentRunnerFilteringTest.java new file mode 100644 index 0000000..9bf542f --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/ParentRunnerFilteringTest.java @@ -0,0 +1,171 @@ +package org.junit.tests.running.classes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.hasSingleFailureContaining; +import static org.junit.runner.Description.createSuiteDescription; +import static org.junit.runner.Description.createTestDescription; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +public class ParentRunnerFilteringTest { + private static Filter notThisMethodName(final String methodName) { + return new Filter() { + @Override + public boolean shouldRun(Description description) { + return description.getMethodName() == null + || !description.getMethodName().equals(methodName); + } + + @Override + public String describe() { + return "don't run method name: " + methodName; + } + }; + } + + private static class CountingFilter extends Filter { + private final Map<Description, Integer> countMap= new HashMap<Description, Integer>(); + + @Override + public boolean shouldRun(Description description) { + Integer count= countMap.get(description); + if (count == null) { + countMap.put(description, 1); + } else { + countMap.put(description, count + 1); + } + return true; + } + + @Override + public String describe() { + return "filter counter"; + } + + public int getCount(final Description desc) { + if (!countMap.containsKey(desc)) { + throw new IllegalArgumentException("Looking for " + desc + + ", but only contains: " + countMap.keySet()); + } + return countMap.get(desc); + } + } + + public static class ExampleTest { + @Test + public void test1() throws Exception { + // passes + } + } + + @RunWith(Suite.class) + @SuiteClasses({ ExampleTest.class }) + public static class ExampleSuite { + } + + @Test + public void testSuiteFiltering() throws Exception { + Runner runner= Request.aClass(ExampleSuite.class).getRunner(); + Filter filter= notThisMethodName("test1"); + try { + filter.apply(runner); + } catch (NoTestsRemainException e) { + return; + } + fail("Expected 'NoTestsRemainException' due to complete filtering"); + } + + public static class SuiteWithUnmodifyableChildList extends Suite { + + public SuiteWithUnmodifyableChildList( + Class<?> klass, RunnerBuilder builder) + throws InitializationError { + super(klass, builder); + } + + @Override + protected List<Runner> getChildren() { + return Collections.unmodifiableList(super.getChildren()); + } + } + + @RunWith(SuiteWithUnmodifyableChildList.class) + @SuiteClasses({ ExampleTest.class }) + public static class ExampleSuiteWithUnmodifyableChildList { + } + + @Test + public void testSuiteFilteringWithUnmodifyableChildList() throws Exception { + Runner runner= Request.aClass(ExampleSuiteWithUnmodifyableChildList.class) + .getRunner(); + Filter filter= notThisMethodName("test1"); + try { + filter.apply(runner); + } catch (NoTestsRemainException e) { + return; + } + fail("Expected 'NoTestsRemainException' due to complete filtering"); + } + + @Test + public void testRunSuiteFiltering() throws Exception { + Request request= Request.aClass(ExampleSuite.class); + Request requestFiltered= request.filterWith(notThisMethodName("test1")); + assertThat(testResult(requestFiltered), + hasSingleFailureContaining("don't run method name: test1")); + } + + @Test + public void testCountClassFiltering() throws Exception { + JUnitCore junitCore= new JUnitCore(); + Request request= Request.aClass(ExampleTest.class); + CountingFilter countingFilter= new CountingFilter(); + Request requestFiltered= request.filterWith(countingFilter); + Result result= junitCore.run(requestFiltered); + assertEquals(1, result.getRunCount()); + assertEquals(0, result.getFailureCount()); + + Description desc= createTestDescription(ExampleTest.class, "test1"); + assertEquals(1, countingFilter.getCount(desc)); + } + + @Test + public void testCountSuiteFiltering() throws Exception { + Class<ExampleSuite> suiteClazz= ExampleSuite.class; + Class<ExampleTest> clazz= ExampleTest.class; + + JUnitCore junitCore= new JUnitCore(); + Request request= Request.aClass(suiteClazz); + CountingFilter countingFilter= new CountingFilter(); + Request requestFiltered= request.filterWith(countingFilter); + Result result= junitCore.run(requestFiltered); + assertEquals(1, result.getRunCount()); + assertEquals(0, result.getFailureCount()); + + Description suiteDesc= createSuiteDescription(clazz); + assertEquals(1, countingFilter.getCount(suiteDesc)); + + Description desc= createTestDescription(ExampleTest.class, "test1"); + assertEquals(1, countingFilter.getCount(desc)); + } +}
\ No newline at end of file diff --git a/junit4/src/test/java/org/junit/tests/running/classes/ParentRunnerTest.java b/junit4/src/test/java/org/junit/tests/running/classes/ParentRunnerTest.java new file mode 100644 index 0000000..21c22b8 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/ParentRunnerTest.java @@ -0,0 +1,139 @@ +package org.junit.tests.running.classes; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.List; + +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.internal.matchers.TypeSafeMatcher; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.ParentRunner; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerScheduler; +import org.junit.tests.experimental.rules.RuleFieldValidatorTest.TestWithNonStaticClassRule; +import org.junit.tests.experimental.rules.RuleFieldValidatorTest.TestWithProtectedClassRule; + +public class ParentRunnerTest { + public static String log= ""; + + public static class FruitTest { + @Test + public void apple() { + log+= "apple "; + } + + @Test + public void banana() { + log+= "banana "; + } + } + + @Test + public void useChildHarvester() throws InitializationError { + log= ""; + ParentRunner<?> runner= new BlockJUnit4ClassRunner(FruitTest.class); + runner.setScheduler(new RunnerScheduler() { + public void schedule(Runnable childStatement) { + log+= "before "; + childStatement.run(); + log+= "after "; + } + + public void finished() { + log+= "afterAll "; + } + }); + + runner.run(new RunNotifier()); + assertEquals("before apple after before banana after afterAll ", log); + } + + @Test + public void testMultipleFilters() throws Exception { + JUnitCore junitCore= new JUnitCore(); + Request request= Request.aClass(ExampleTest.class); + Request requestFiltered= request.filterWith(new Exclude("test1")); + Request requestFilteredFiltered= requestFiltered + .filterWith(new Exclude("test2")); + Result result= junitCore.run(requestFilteredFiltered); + assertThat(result.getFailures(), isEmpty()); + assertEquals(1, result.getRunCount()); + } + + private Matcher<List<?>> isEmpty() { + return new TypeSafeMatcher<List<?>>() { + public void describeTo(org.hamcrest.Description description) { + description.appendText("is empty"); + } + + @Override + public boolean matchesSafely(List<?> item) { + return item.size() == 0; + } + }; + } + + private static class Exclude extends Filter { + private String methodName; + + public Exclude(String methodName) { + this.methodName= methodName; + } + + @Override + public boolean shouldRun(Description description) { + return !description.getMethodName().equals(methodName); + } + + @Override + public String describe() { + return "filter method name: " + methodName; + } + } + + public static class ExampleTest { + @Test + public void test1() throws Exception { + } + + @Test + public void test2() throws Exception { + } + + @Test + public void test3() throws Exception { + } + } + + @Test + public void failWithHelpfulMessageForProtectedClassRule() { + assertClassHasFailureMessage(TestWithProtectedClassRule.class, + "The @ClassRule 'temporaryFolder' must be public."); + } + + @Test + public void failWithHelpfulMessageForNonStaticClassRule() { + assertClassHasFailureMessage(TestWithNonStaticClassRule.class, + "The @ClassRule 'temporaryFolder' must be static."); + } + + private void assertClassHasFailureMessage(Class<?> klass, String message) { + JUnitCore junitCore= new JUnitCore(); + Request request= Request.aClass(klass); + Result result= junitCore.run(request); + assertThat(result.getFailureCount(), is(2)); //the second failure is no runnable methods + assertThat(result.getFailures().get(0).getMessage(), + is(equalTo(message))); + + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/classes/RunWithTest.java b/junit4/src/test/java/org/junit/tests/running/classes/RunWithTest.java new file mode 100644 index 0000000..1df0167 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/RunWithTest.java @@ -0,0 +1,85 @@ +package org.junit.tests.running.classes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; + +public class RunWithTest { + + private static String log; + + public static class ExampleRunner extends Runner { + public ExampleRunner(Class<?> klass) { + log+= "initialize"; + } + + @Override + public void run(RunNotifier notifier) { + log+= "run"; + } + + @Override + public int testCount() { + log+= "count"; + return 0; + } + + @Override + public Description getDescription() { + log+= "plan"; + return Description.createSuiteDescription("example"); + } + } + + @RunWith(ExampleRunner.class) + public static class ExampleTest { + } + + @Test public void run() { + log= ""; + + JUnitCore.runClasses(ExampleTest.class); + assertTrue(log.contains("plan")); + assertTrue(log.contains("initialize")); + assertTrue(log.contains("run")); + } + + public static class SubExampleTest extends ExampleTest { + } + + @Test public void runWithExtendsToSubclasses() { + log= ""; + + JUnitCore.runClasses(SubExampleTest.class); + assertTrue(log.contains("run")); + } + + public static class BadRunner extends Runner { + @Override + public Description getDescription() { + return null; + } + + @Override + public void run(RunNotifier notifier) { + // do nothing + } + } + + @RunWith(BadRunner.class) + public static class Empty { + } + + @Test + public void characterizeErrorMessageFromBadRunner() { + assertEquals( + "Custom runner class BadRunner should have a public constructor with signature BadRunner(Class testClass)", + JUnitCore.runClasses(Empty.class).getFailures().get(0) + .getMessage()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/classes/SuiteTest.java b/junit4/src/test/java/org/junit/tests/running/classes/SuiteTest.java new file mode 100644 index 0000000..bda24fd --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/SuiteTest.java @@ -0,0 +1,167 @@ +package org.junit.tests.running.classes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.experimental.results.PrintableResult.testResult; +import static org.junit.experimental.results.ResultMatchers.hasSingleFailureContaining; + +import java.util.List; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestResult; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.Runner; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +public class SuiteTest { + public static class TestA { + @Test public void pass() { + } + } + + public static class TestB { + @Test public void fail() { + Assert.fail(); + } + } + + @RunWith(Suite.class) + @SuiteClasses({TestA.class, TestB.class}) + public static class All { + } + + public static class InheritsAll extends All { + } + + @Test public void ensureTestIsRun() { + JUnitCore core= new JUnitCore(); + Result result= core.run(All.class); + assertEquals(2, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + } + + @Test public void ensureInheritedTestIsRun() { + JUnitCore core= new JUnitCore(); + Result result= core.run(InheritsAll.class); + assertEquals(2, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + } + + @Test public void suiteTestCountIsCorrect() throws Exception { + Runner runner= Request.aClass(All.class).getRunner(); + assertEquals(2, runner.testCount()); + } + + @Test public void ensureSuitesWorkWithForwardCompatibility() { + junit.framework.Test test= new JUnit4TestAdapter(All.class); + TestResult result= new TestResult(); + test.run(result); + assertEquals(2, result.runCount()); + } + + @Test public void forwardCompatibilityWorksWithGetTests() { + JUnit4TestAdapter adapter= new JUnit4TestAdapter(All.class); + List<? extends junit.framework.Test> tests= adapter.getTests(); + assertEquals(2, tests.size()); + } + + @Test public void forwardCompatibilityWorksWithTestCount() { + JUnit4TestAdapter adapter= new JUnit4TestAdapter(All.class); + assertEquals(2, adapter.countTestCases()); + } + + + private static String log= ""; + @RunWith(Suite.class) + @SuiteClasses({TestA.class, TestB.class}) + public static class AllWithBeforeAndAfterClass { + @BeforeClass public static void before() { log+= "before "; } + @AfterClass public static void after() { log+= "after "; } + } + + @Test public void beforeAndAfterClassRunOnSuite() { + log= ""; + JUnitCore.runClasses(AllWithBeforeAndAfterClass.class); + assertEquals("before after ", log); + } + + @RunWith(Suite.class) + public static class AllWithOutAnnotation { + } + + @Test public void withoutSuiteClassAnnotationProducesFailure() { + Result result= JUnitCore.runClasses(AllWithOutAnnotation.class); + assertEquals(1, result.getFailureCount()); + String expected= String.format( + "class '%s' must have a SuiteClasses annotation", + AllWithOutAnnotation.class.getName()); + assertEquals(expected, result.getFailures().get(0).getMessage()); + } + + @RunWith(Suite.class) + @SuiteClasses(InfiniteLoop.class) + static public class InfiniteLoop { } + + @Test public void whatHappensWhenASuiteHasACycle() { + Result result= JUnitCore.runClasses(InfiniteLoop.class); + assertEquals(1, result.getFailureCount()); + } + + @RunWith(Suite.class) + @SuiteClasses({BiInfiniteLoop.class, BiInfiniteLoop.class}) + static public class BiInfiniteLoop { } + + @Test public void whatHappensWhenASuiteHasAForkingCycle() { + Result result= JUnitCore.runClasses(BiInfiniteLoop.class); + assertEquals(2, result.getFailureCount()); + } + + // The interesting case here is that Hydra indirectly contains two copies of + // itself (if it only contains one, Java's StackOverflowError eventually + // bails us out) + + @RunWith(Suite.class) + @SuiteClasses({Hercules.class}) + static public class Hydra { } + + @RunWith(Suite.class) + @SuiteClasses({Hydra.class, Hydra.class}) + static public class Hercules { } + + @Test public void whatHappensWhenASuiteContainsItselfIndirectly() { + Result result= JUnitCore.runClasses(Hydra.class); + assertEquals(2, result.getFailureCount()); + } + + @RunWith(Suite.class) + @SuiteClasses( {}) + public class WithoutDefaultConstructor { + public WithoutDefaultConstructor(int i) { + + } + } + + @Test + public void suiteShouldBeOKwithNonDefaultConstructor() throws Exception { + Result result= JUnitCore.runClasses(WithoutDefaultConstructor.class); + assertTrue(result.wasSuccessful()); + } + + @RunWith(Suite.class) + public class NoSuiteClassesAnnotation { + } + + @Test + public void suiteShouldComplainAboutNoSuiteClassesAnnotation() { + assertThat(testResult(NoSuiteClassesAnnotation.class), hasSingleFailureContaining("SuiteClasses")); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/classes/TestClassTest.java b/junit4/src/test/java/org/junit/tests/running/classes/TestClassTest.java new file mode 100644 index 0000000..7b738d9 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/TestClassTest.java @@ -0,0 +1,121 @@ +package org.junit.tests.running.classes; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runners.model.TestClass; + +public class TestClassTest { + public static class TwoConstructors { + public TwoConstructors() { + } + + public TwoConstructors(int x) { + } + } + + @Test(expected= IllegalArgumentException.class) + public void complainIfMultipleConstructors() { + new TestClass(TwoConstructors.class); + } + + public static class ManyMethods { + @Test + public void a() { + } + + @Before + public void b() { + } + + @Ignore + @Test + public void c() { + } + + @Ignore + @After + public void d() { + } + + public void e() { + } + + @BeforeClass + public void f() { + } + + public void g() { + } + + @AfterClass + public void h() { + } + + @Test + public void i() { + } + + @Test + public void j() { + } + } + + public static class SuperclassWithField { + @Rule + public TestRule x; + } + + public static class SubclassWithField extends SuperclassWithField { + @Rule + public TestRule x; + } + + @Test + public void fieldsOnSubclassesShadowSuperclasses() { + assertThat(new TestClass(SubclassWithField.class).getAnnotatedFields( + Rule.class).size(), is(1)); + } + + public static class OuterClass { + public class NonStaticInnerClass { + } + } + + @Test + public void identifyNonStaticInnerClass() { + assertThat( + new TestClass(OuterClass.NonStaticInnerClass.class) + .isANonStaticInnerClass(), + is(true)); + } + + public static class OuterClass2 { + public static class StaticInnerClass { + } + } + + @Test + public void dontMarkStaticInnerClassAsNonStatic() { + assertThat( + new TestClass(OuterClass2.StaticInnerClass.class) + .isANonStaticInnerClass(), + is(false)); + } + + public static class SimpleClass { + } + + @Test + public void dontMarkNonInnerClassAsInnerClass() { + assertThat(new TestClass(SimpleClass.class).isANonStaticInnerClass(), + is(false)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/classes/UseSuiteAsASuperclassTest.java b/junit4/src/test/java/org/junit/tests/running/classes/UseSuiteAsASuperclassTest.java new file mode 100644 index 0000000..2917181 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/classes/UseSuiteAsASuperclassTest.java @@ -0,0 +1,44 @@ +package org.junit.tests.running.classes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.model.InitializationError; + +public class UseSuiteAsASuperclassTest { + + public static class TestA { + @Test + public void pass() { + } + } + + public static class TestB { + @Test + public void dontPass() { + fail(); + } + } + + public static class MySuite extends Suite { + public MySuite(Class<?> klass) throws InitializationError { + super(klass, new Class[] { TestA.class, TestB.class }); + } + } + + @RunWith(MySuite.class) + public static class AllWithMySuite { + } + + @Test + public void ensureTestsAreRun() { + JUnitCore core= new JUnitCore(); + Result result= core.run(AllWithMySuite.class); + assertEquals(2, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/core/CommandLineTest.java b/junit4/src/test/java/org/junit/tests/running/core/CommandLineTest.java new file mode 100644 index 0000000..cdf8b55 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/core/CommandLineTest.java @@ -0,0 +1,67 @@ +package org.junit.tests.running.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.internal.RealSystem; +import org.junit.runner.JUnitCore; + +public class CommandLineTest { + private ByteArrayOutputStream results; + private PrintStream oldOut; + private static boolean testWasRun; + + @Before public void before() { + oldOut= System.out; + results= new ByteArrayOutputStream(); + System.setOut(new PrintStream(results)); + } + + @After public void after() { + System.setOut(oldOut); + } + + static public class Example { + @Test public void test() { + testWasRun= true; + } + } + + @Test public void runATest() { + testWasRun= false; // todo create a TestSystem instead + new JUnitCore().runMain(new RealSystem(), new String[]{"org.junit.tests.running.core.CommandLineTest$Example"}); + assertTrue(testWasRun); + } + + @Test public void runAClass() { + testWasRun= false; + JUnitCore.runClasses(Example.class); + assertTrue(testWasRun); + } + + private static int fCount; + + static public class Count { + @Test public void increment() { + fCount++; + } + } + + @Test public void runTwoClassesAsArray() { + fCount= 0; + JUnitCore.runClasses(new Class[] {Count.class, Count.class}); + assertEquals(2, fCount); + } + + @Test public void runTwoClasses() { + fCount= 0; + JUnitCore.runClasses(Count.class, Count.class); + assertEquals(2, fCount); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/core/JUnitCoreReturnsCorrectExitCodeTest.java b/junit4/src/test/java/org/junit/tests/running/core/JUnitCoreReturnsCorrectExitCodeTest.java new file mode 100644 index 0000000..0d8bb37 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/core/JUnitCoreReturnsCorrectExitCodeTest.java @@ -0,0 +1,39 @@ +package org.junit.tests.running.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.tests.TestSystem; + +public class JUnitCoreReturnsCorrectExitCodeTest { + + static public class Fail { + @Test public void kaboom() { + fail(); + } + } + + @Test public void failureCausesExitCodeOf1() throws Exception { + runClass(getClass().getName() + "$Fail", 1); + } + + @Test public void missingClassCausesExitCodeOf1() throws Exception { + runClass("Foo", 1); + } + + static public class Succeed { + @Test public void peacefulSilence() { + } + } + + @Test public void successCausesExitCodeOf0() throws Exception { + runClass(getClass().getName() + "$Succeed", 0); + } + + private void runClass(String className, int returnCode) { + TestSystem system= new TestSystem(); + JUnitCore.runMainAndExit(system, className); + assertEquals(returnCode, system.fCode); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/core/SystemExitTest.java b/junit4/src/test/java/org/junit/tests/running/core/SystemExitTest.java new file mode 100644 index 0000000..1460119 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/core/SystemExitTest.java @@ -0,0 +1,30 @@ +package org.junit.tests.running.core; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.InputStream; + +import org.junit.Test; + +// Make sure System.exit works as expected. We've had problems with this on some platforms. +public class SystemExitTest { + + private static final int EXIT_CODE= 5; + + static public class Exit { + public static void main(String[] args) { + System.exit(EXIT_CODE); + } + } + + @Test public void failureCausesExitCodeOf1() throws Exception { + String java= System.getProperty("java.home")+File.separator+"bin"+File.separator+"java"; + String classPath= getClass().getClassLoader().getResource(".").getFile() + File.pathSeparator + System.getProperty("java.class.path"); + String [] cmd= { java, "-cp", classPath, getClass().getName() + "$Exit"}; + Process process= Runtime.getRuntime().exec(cmd); + InputStream input= process.getInputStream(); + while((input.read()) != -1); + assertEquals(EXIT_CODE, process.waitFor()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/methods/AnnotationTest.java b/junit4/src/test/java/org/junit/tests/running/methods/AnnotationTest.java new file mode 100644 index 0000000..2eb496a --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/methods/AnnotationTest.java @@ -0,0 +1,520 @@ +package org.junit.tests.running.methods; + +import static org.junit.Assert.assertThat; +import static org.junit.matchers.JUnitMatchers.both; +import static org.junit.matchers.JUnitMatchers.containsString; + +import java.util.Collection; +import java.util.HashSet; + +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +public class AnnotationTest extends TestCase { + static boolean run; + + @Override + public void setUp() { + run= false; + } + + static public class SimpleTest { + @Test public void success() { + run= true; + } + } + + public void testAnnotatedMethod() throws Exception { + JUnitCore runner= new JUnitCore(); + runner.run(SimpleTest.class); + assertTrue(run); + } + + @RunWith(JUnit4.class) + static public class SimpleTestWithFutureProofExplicitRunner { + @Test public void success() { + run= true; + } + } + + public void testAnnotatedMethodWithFutureProofExplicitRunner() throws Exception { + JUnitCore runner= new JUnitCore(); + runner.run(SimpleTestWithFutureProofExplicitRunner.class); + assertTrue(run); + } + + static public class SetupTest { + @Before public void before() { + run= true; + } + @Test public void success() { + } + } + + public void testSetup() throws Exception { + JUnitCore runner= new JUnitCore(); + runner.run(SetupTest.class); + assertTrue(run); + } + + static public class TeardownTest { + @After public void after() { + run= true; + } + @Test public void success() { + } + } + + public void testTeardown() throws Exception { + JUnitCore runner= new JUnitCore(); + runner.run(TeardownTest.class); + assertTrue(run); + } + + static public class FailureTest { + @Test public void error() throws Exception { + org.junit.Assert.fail(); + } + } + + public void testRunFailure() throws Exception { + JUnitCore runner= new JUnitCore(); + Result result= runner.run(FailureTest.class); + assertEquals(1, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + assertEquals(AssertionError.class, result.getFailures().get(0).getException().getClass()); + } + + static public class SetupFailureTest { + @Before public void before() { + throw new Error(); + } + @Test public void test() { + run= true; + } + } + + public void testSetupFailure() throws Exception { + JUnitCore core= new JUnitCore(); + Result runner= core.run(SetupFailureTest.class); + assertEquals(1, runner.getRunCount()); + assertEquals(1, runner.getFailureCount()); + assertEquals(Error.class, runner.getFailures().get(0).getException().getClass()); + assertFalse(run); + } + + static public class TeardownFailureTest { + @After public void after() { + throw new Error(); + } + @Test public void test() { + } + } + + public void testTeardownFailure() throws Exception { + JUnitCore core= new JUnitCore(); + Result runner= core.run(TeardownFailureTest.class); + assertEquals(1, runner.getRunCount()); + assertEquals(1, runner.getFailureCount()); + assertEquals(Error.class, runner.getFailures().get(0).getException().getClass()); + } + + static public class TestAndTeardownFailureTest { + @After public void after() { + throw new Error("hereAfter"); + } + @Test public void test() throws Exception { + throw new Exception("inTest"); + } + } + + public void testTestAndTeardownFailure() throws Exception { + JUnitCore core= new JUnitCore(); + Result runner= core.run(TestAndTeardownFailureTest.class); + assertEquals(1, runner.getRunCount()); + assertEquals(2, runner.getFailureCount()); + assertThat(runner.getFailures().toString(), both(containsString("hereAfter")).and(containsString("inTest"))); + } + + static public class TeardownAfterFailureTest { + @After public void after() { + run= true; + } + @Test public void test() throws Exception { + throw new Exception(); + } + } + + public void testTeardownAfterFailure() throws Exception { + JUnitCore runner= new JUnitCore(); + runner.run(TeardownAfterFailureTest.class); + assertTrue(run); + } + + static int count; + static Collection<Object> tests; + static public class TwoTests { + @Test public void one() { + count++; + tests.add(this); + } + @Test public void two() { + count++; + tests.add(this); + } + } + + public void testTwoTests() throws Exception { + count= 0; + tests= new HashSet<Object>(); + JUnitCore runner= new JUnitCore(); + runner.run(TwoTests.class); + assertEquals(2, count); + assertEquals(2, tests.size()); + } + + static public class OldTest extends TestCase { + public void test() { + run= true; + } + } + public void testOldTest() throws Exception { + JUnitCore runner= new JUnitCore(); + runner.run(OldTest.class); + assertTrue(run); + } + + static public class OldSuiteTest extends TestCase { + public void testOne() { + run= true; + } + } + + public void testOldSuiteTest() throws Exception { + TestSuite suite= new TestSuite(OldSuiteTest.class); + JUnitCore runner= new JUnitCore(); + runner.run(suite); + assertTrue(run); + } + + static public class ExceptionTest { + @Test(expected= Error.class) public void expectedException() { + throw new Error(); + } + } + + public void testException() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(ExceptionTest.class); + assertEquals(0, result.getFailureCount()); + } + + static public class NoExceptionTest { + @Test(expected= Error.class) + public void expectedException() { + } + } + + public void testExceptionNotThrown() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(NoExceptionTest.class); + assertEquals(1, result.getFailureCount()); + assertEquals("Expected exception: java.lang.Error", result.getFailures().get(0).getMessage()); + } + + static public class OneTimeSetup { + @BeforeClass public static void once() { + count++; + } + @Test public void one() {} + @Test public void two() {} + } + + public void testOneTimeSetup() throws Exception { + count= 0; + JUnitCore core= new JUnitCore(); + core.run(OneTimeSetup.class); + assertEquals(1, count); + } + + static public class OneTimeTeardown { + @AfterClass public static void once() { + count++; + } + @Test public void one() {} + @Test public void two() {} + } + + public void testOneTimeTeardown() throws Exception { + count= 0; + JUnitCore core= new JUnitCore(); + core.run(OneTimeTeardown.class); + assertEquals(1, count); + } + + static String log; + + public static class OrderTest { + @BeforeClass public static void onceBefore() { log+= "beforeClass "; } + @Before public void before() { log+= "before "; } + @Test public void test() { log+= "test "; } + @After public void after() { log+= "after "; } + @AfterClass public static void onceAfter() { log+= "afterClass "; } + } + + public void testOrder() throws Exception { + log= ""; + JUnitCore core= new JUnitCore(); + core.run(OrderTest.class); + assertEquals("beforeClass before test after afterClass ", log); + } + + static public class NonStaticOneTimeSetup { + @BeforeClass public void once() { + } + + @Test public void aTest() { + } + } + + public void testNonStaticOneTimeSetup() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(NonStaticOneTimeSetup.class); + assertEquals(1, result.getFailureCount()); + } + + static public class ErrorInBeforeClass { + @BeforeClass public static void before() throws Exception { + throw new Exception(); + } + @Test public void test() { + run= true; + } + } + + public void testErrorInBeforeClass() throws Exception { + run= false; + JUnitCore core= new JUnitCore(); + Result result= core.run(ErrorInBeforeClass.class); + assertFalse(run); + assertEquals(1, result.getFailureCount()); + Description description= result.getFailures().get(0).getDescription(); + assertEquals(ErrorInBeforeClass.class.getName(), description.getDisplayName()); + } + + static public class ErrorInAfterClass { + @Test public void test() { + run= true; + } + @AfterClass public static void after() throws Exception { + throw new Exception(); + } + } + + public void testErrorInAfterClass() throws Exception { + run= false; + JUnitCore core= new JUnitCore(); + Result result= core.run(ErrorInAfterClass.class); + assertTrue(run); + assertEquals(1, result.getFailureCount()); + } + + static public class SuperInheritance { + @BeforeClass static public void beforeClassSuper() { + log+= "Before class super "; + } + @AfterClass static public void afterClassSuper() { + log+= "After class super "; + } + @Before public void beforeSuper() { + log+= "Before super "; + } + @After public void afterSuper() { + log+= "After super "; + } + } + + static public class SubInheritance extends SuperInheritance { + @BeforeClass static public void beforeClassSub() { + log+= "Before class sub "; + } + @AfterClass static public void afterClassSub() { + log+= "After class sub "; + } + @Before public void beforeSub() { + log+= "Before sub "; + } + @After public void afterSub() { + log+= "After sub "; + } + @Test public void test() { + log+= "Test "; + } + } + + public void testOrderingOfInheritance() throws Exception { + log= ""; + JUnitCore core= new JUnitCore(); + core.run(SubInheritance.class); + assertEquals("Before class super Before class sub Before super Before sub Test After sub After super After class sub After class super ", log); + } + + static public class SuperShadowing { + @Before public void before() { + log+= "Before super "; + } + @After public void after() { + log+= "After super "; + } + } + + static public class SubShadowing extends SuperShadowing { + @Override + @Before public void before() { + log+= "Before sub "; + } + @Override + @After public void after() { + log+= "After sub "; + } + @Test public void test() { + log+= "Test "; + } + } + + public void testShadowing() throws Exception { + log= ""; + JUnitCore core= new JUnitCore(); + core.run(SubShadowing.class); + assertEquals("Before sub Test After sub ", log); + } + + static public class SuperTest { + @Test public void one() { + log+= "Super"; + } + + @Test public void two() { + log+= "Two"; + } + } + + static public class SubTest extends SuperTest { + @Override + @Test public void one() { + log+= "Sub"; + } + } + + public void testTestInheritance() throws Exception { + log= ""; + JUnitCore core= new JUnitCore(); + core.run(SubTest.class); + // The order in which the test methods are called is unspecified + assertTrue(log.contains("Sub")); + assertTrue(log.contains("Two")); + assertFalse(log.contains("Super")); + } + + static public class RunAllAfters { + @Before public void good() { + } + @Before public void bad() { + throw new Error(); + } + @Test public void empty() { + } + @After public void one() { + log+= "one"; + } + @After public void two() { + log+= "two"; + } + } + + public void testRunAllAfters() { + log= ""; + JUnitCore core= new JUnitCore(); + core.run(RunAllAfters.class); + assertTrue(log.contains("one")); + assertTrue(log.contains("two")); + } + + static public class RunAllAftersRegardless { + @Test public void empty() { + } + @After public void one() { + log+= "one"; + throw new Error(); + } + @After public void two() { + log+= "two"; + throw new Error(); + } + } + + public void testRunAllAftersRegardless() { + log= ""; + JUnitCore core= new JUnitCore(); + Result result= core.run(RunAllAftersRegardless.class); + assertTrue(log.contains("one")); + assertTrue(log.contains("two")); + assertEquals(2, result.getFailureCount()); + } + + static public class RunAllAfterClasses { + @Before public void good() { + } + @BeforeClass public static void bad() { + throw new Error(); + } + @Test public void empty() { + } + @AfterClass public static void one() { + log+= "one"; + } + @AfterClass public static void two() { + log+= "two"; + } + } + + public void testRunAllAfterClasses() { + log= ""; + JUnitCore core= new JUnitCore(); + core.run(RunAllAfterClasses.class); + assertTrue(log.contains("one")); + assertTrue(log.contains("two")); + } + + static public class RunAllAfterClassesRegardless { + @Test public void empty() { + } + @AfterClass static public void one() { + log+= "one"; + throw new Error(); + } + @AfterClass static public void two() { + log+= "two"; + throw new Error(); + } + } + + public void testRunAllAfterClassesRegardless() { + log= ""; + JUnitCore core= new JUnitCore(); + Result result= core.run(RunAllAfterClassesRegardless.class); + assertTrue(log.contains("one")); + assertTrue(log.contains("two")); + assertEquals(2, result.getFailureCount()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/methods/ExpectedTest.java b/junit4/src/test/java/org/junit/tests/running/methods/ExpectedTest.java new file mode 100644 index 0000000..d16e689 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/methods/ExpectedTest.java @@ -0,0 +1,58 @@ +package org.junit.tests.running.methods; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; + +public class ExpectedTest { + + public static class Expected { + @Test(expected= Exception.class) public void expected() throws Exception { + throw new Exception(); + } + } + + @Test public void expected() { + JUnitCore core= new JUnitCore(); + Result result= core.run(Expected.class); + assertTrue(result.wasSuccessful()); + } + + public static class Unexpected { + @Test(expected= Exception.class) public void expected() throws Exception { + throw new Error(); + } + } + @Test public void unexpected() { + Result result= JUnitCore.runClasses(Unexpected.class); + Failure failure= result.getFailures().get(0); + String message= failure.getMessage(); + assertTrue(message.contains("expected<java.lang.Exception> but was<java.lang.Error>")); + assertEquals(Error.class, failure.getException().getCause().getClass()); + } + + public static class NoneThrown { + @Test(expected= Exception.class) public void nothing() { + } + } + @Test public void noneThrown() { + JUnitCore core= new JUnitCore(); + Result result= core.run(NoneThrown.class); + assertFalse(result.wasSuccessful()); + String message= result.getFailures().get(0).getMessage(); + assertTrue(message.contains("Expected exception: java.lang.Exception")); + } + + public static class ExpectSuperclass { + @Test(expected= RuntimeException.class) public void throwsSubclass() { + throw new ClassCastException(); + } + } + @Test public void expectsSuperclass() { + assertTrue(new JUnitCore().run(ExpectSuperclass.class).wasSuccessful()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/methods/InheritedTestTest.java b/junit4/src/test/java/org/junit/tests/running/methods/InheritedTestTest.java new file mode 100644 index 0000000..15f55a6 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/methods/InheritedTestTest.java @@ -0,0 +1,31 @@ +package org.junit.tests.running.methods; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; + +public class InheritedTestTest { + public abstract static class Super { + @Test public void nothing() {} + } + public static class Sub extends Super {} + + @Test public void subclassWithOnlyInheritedTestsRuns() { + Result result= JUnitCore.runClasses(Sub.class); + assertTrue(result.wasSuccessful()); + } + + public static class SubWithBefore extends Super { + @Before public void gack() { + fail(); + } + } + + @Test public void subclassWithInheritedTestAndOwnBeforeRunsBefore() { + assertFalse(JUnitCore.runClasses(SubWithBefore.class).wasSuccessful()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/methods/ParameterizedTestMethodTest.java b/junit4/src/test/java/org/junit/tests/running/methods/ParameterizedTestMethodTest.java new file mode 100644 index 0000000..4d4dcbd --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/methods/ParameterizedTestMethodTest.java @@ -0,0 +1,102 @@ +package org.junit.tests.running.methods; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import junit.framework.JUnit4TestAdapter; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.model.InitializationError; + +@RunWith(Parameterized.class) +public class ParameterizedTestMethodTest { + + @SuppressWarnings("all") + public static class EverythingWrong { + private EverythingWrong() {} + @BeforeClass public void notStaticBC() {} + @BeforeClass static void notPublicBC() {} + @BeforeClass public static int nonVoidBC() { return 0; } + @BeforeClass public static void argumentsBC(int i) {} + @BeforeClass public static void fineBC() {} + @AfterClass public void notStaticAC() {} + @AfterClass static void notPublicAC() {} + @AfterClass public static int nonVoidAC() { return 0; } + @AfterClass public static void argumentsAC(int i) {} + @AfterClass public static void fineAC() {} + @After public static void staticA() {} + @After void notPublicA() {} + @After public int nonVoidA() { return 0; } + @After public void argumentsA(int i) {} + @After public void fineA() {} + @Before public static void staticB() {} + @Before void notPublicB() {} + @Before public int nonVoidB() { return 0; } + @Before public void argumentsB(int i) {} + @Before public void fineB() {} + @Test public static void staticT() {} + @Test void notPublicT() {} + @Test public int nonVoidT() { return 0; } + @Test public void argumentsT(int i) {} + @Test public void fineT() {} + } + + private Class<?> fClass; + private int fErrorCount; + + static public class SuperWrong { + @Test void notPublic() { + } + } + + static public class SubWrong extends SuperWrong { + @Test public void justFine() { + } + } + + static public class SubShadows extends SuperWrong { + @Override + @Test public void notPublic() { + } + } + + public ParameterizedTestMethodTest(Class<?> class1, int errorCount) { + fClass= class1; + fErrorCount= errorCount; + } + + @Parameters + public static Collection<Object[]> params() { + return Arrays.asList(new Object[][] { + { EverythingWrong.class, 1 + 4 * 5 }, { SubWrong.class, 1 }, + { SubShadows.class, 0 } }); + } + + private List<Throwable> validateAllMethods(Class<?> clazz) { + try { + new BlockJUnit4ClassRunner(clazz); + } catch (InitializationError e) { + return e.getCauses(); + } + return Collections.emptyList(); + } + + @Test public void testFailures() throws Exception { + List<Throwable> problems= validateAllMethods(fClass); + assertEquals(fErrorCount, problems.size()); + } + public static junit.framework.Test suite() { + return new JUnit4TestAdapter(ParameterizedTestMethodTest.class); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/methods/TestMethodTest.java b/junit4/src/test/java/org/junit/tests/running/methods/TestMethodTest.java new file mode 100644 index 0000000..cbabab2 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/methods/TestMethodTest.java @@ -0,0 +1,147 @@ +package org.junit.tests.running.methods; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Collections; +import java.util.List; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestResult; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; + +public class TestMethodTest { + + @SuppressWarnings("all") + public static class EverythingWrong { + private EverythingWrong() {} + @BeforeClass public void notStaticBC() {} + @BeforeClass static void notPublicBC() {} + @BeforeClass public static int nonVoidBC() { return 0; } + @BeforeClass public static void argumentsBC(int i) {} + @BeforeClass public static void fineBC() {} + @AfterClass public void notStaticAC() {} + @AfterClass static void notPublicAC() {} + @AfterClass public static int nonVoidAC() { return 0; } + @AfterClass public static void argumentsAC(int i) {} + @AfterClass public static void fineAC() {} + @After public static void staticA() {} + @After void notPublicA() {} + @After public int nonVoidA() { return 0; } + @After public void argumentsA(int i) {} + @After public void fineA() {} + @Before public static void staticB() {} + @Before void notPublicB() {} + @Before public int nonVoidB() { return 0; } + @Before public void argumentsB(int i) {} + @Before public void fineB() {} + @Test public static void staticT() {} + @Test void notPublicT() {} + @Test public int nonVoidT() { return 0; } + @Test public void argumentsT(int i) {} + @Test public void fineT() {} + } + + @Test public void testFailures() throws Exception { + List<Throwable> problems= validateAllMethods(EverythingWrong.class); + int errorCount= 1 + 4 * 5; // missing constructor plus four invalid methods for each annotation */ + assertEquals(errorCount, problems.size()); + } + + static public class SuperWrong { + @Test void notPublic() { + } + } + + static public class SubWrong extends SuperWrong { + @Test public void justFine() { + } + } + + @Test public void validateInheritedMethods() throws Exception { + List<Throwable> problems= validateAllMethods(SubWrong.class); + assertEquals(1, problems.size()); + } + + static public class SubShadows extends SuperWrong { + @Override + @Test public void notPublic() { + } + } + + @Test public void dontValidateShadowedMethods() throws Exception { + List<Throwable> problems= validateAllMethods(SubShadows.class); + assertTrue(problems.isEmpty()); + } + + private List<Throwable> validateAllMethods(Class<?> clazz) { + try { + new BlockJUnit4ClassRunner(clazz); + } catch (InitializationError e) { + return e.getCauses(); + } + return Collections.emptyList(); + } + + static public class IgnoredTest { + @Test public void valid() {} + @Ignore @Test public void ignored() {} + @Ignore("For testing purposes") @Test public void withReason() {} + } + + @Test public void ignoreRunner() { + JUnitCore runner= new JUnitCore(); + Result result= runner.run(IgnoredTest.class); + assertEquals(2, result.getIgnoreCount()); + assertEquals(1, result.getRunCount()); + } + + @Test public void compatibility() { + TestResult result= new TestResult(); + new JUnit4TestAdapter(IgnoredTest.class).run(result); + assertEquals(1, result.runCount()); + } + + public static class Confused { + @Test public void a(Object b) { + } + + @Test public void a() { + } + } + + @Test(expected=InitializationError.class) public void overloaded() throws InitializationError { + new BlockJUnit4ClassRunner(Confused.class); + } + + public static class ConstructorParameter { + public ConstructorParameter(Object something) { + } + + @Test public void a() { + } + } + + @Test(expected=InitializationError.class) public void constructorParameter() throws InitializationError { + new BlockJUnit4ClassRunner(ConstructorParameter.class); + } + + public static class OnlyTestIsIgnored { + @Ignore @Test public void ignored() {} + } + + @Test public void onlyIgnoredMethodsIsStillFineTestClass() { + Result result= JUnitCore.runClasses(OnlyTestIsIgnored.class); + assertEquals(0, result.getFailureCount()); + assertEquals(1, result.getIgnoreCount()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/running/methods/TimeoutTest.java b/junit4/src/test/java/org/junit/tests/running/methods/TimeoutTest.java new file mode 100644 index 0000000..777b0bd --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/running/methods/TimeoutTest.java @@ -0,0 +1,170 @@ +package org.junit.tests.running.methods; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.matchers.JUnitMatchers.containsString; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestResult; +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; + +public class TimeoutTest { + + static public class FailureWithTimeoutTest { + @Test(timeout= 1000) public void failure() { + fail(); + } + } + + @Test public void failureWithTimeout() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(FailureWithTimeoutTest.class); + assertEquals(1, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + assertEquals(AssertionError.class, result.getFailures().get(0).getException().getClass()); + } + + static public class FailureWithTimeoutRunTimeExceptionTest { + @Test(timeout= 1000) public void failure() { + throw new NullPointerException(); + } + } + + @Test public void failureWithTimeoutRunTimeException() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(FailureWithTimeoutRunTimeExceptionTest.class); + assertEquals(1, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + assertEquals(NullPointerException.class, result.getFailures().get(0).getException().getClass()); + } + + static public class SuccessWithTimeoutTest { + @Test(timeout= 1000) public void success() { + } + } + + @Test public void successWithTimeout() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(SuccessWithTimeoutTest.class); + assertEquals(1, result.getRunCount()); + assertEquals(0, result.getFailureCount()); + } + + static public class TimeoutFailureTest { + @Test(timeout= 100) public void success() throws InterruptedException { + Thread.sleep(40000); + } + } + + @Ignore("was breaking gump") + @Test public void timeoutFailure() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(TimeoutFailureTest.class); + assertEquals(1, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + assertEquals(InterruptedException.class, result.getFailures().get(0).getException().getClass()); + } + + static public class InfiniteLoopTest { + @Test(timeout= 100) public void failure() { + infiniteLoop(); + } + + private void infiniteLoop() { + for(;;) + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } + } + } + + @Test public void infiniteLoop() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(InfiniteLoopTest.class); + assertEquals(1, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + Throwable exception= result.getFailures().get(0).getException(); + assertTrue(exception.getMessage().contains("test timed out after 100 milliseconds")); + } + + static public class ImpatientLoopTest { + @Test(timeout= 1) public void failure() { + infiniteLoop(); + } + + private void infiniteLoop() { + for(;;); + } + } + + @Ignore("This breaks sporadically with time differences just slightly more than 200ms") + @Test public void infiniteLoopRunsForApproximatelyLengthOfTimeout() throws Exception { + // "prime the pump": running these beforehand makes the runtimes more predictable + // (because of class loading?) + JUnitCore.runClasses(InfiniteLoopTest.class, ImpatientLoopTest.class); + long longTime= runAndTime(InfiniteLoopTest.class); + long shortTime= runAndTime(ImpatientLoopTest.class); + long difference= longTime - shortTime; + assertTrue(String.format("Difference was %sms", difference), difference < 200); + } + + private long runAndTime(Class<?> clazz) { + JUnitCore core= new JUnitCore(); + long startTime= System.currentTimeMillis(); + core.run(clazz); + long totalTime = System.currentTimeMillis() - startTime; + return totalTime; + } + + @Test public void stalledThreadAppearsInStackTrace() throws Exception { + JUnitCore core= new JUnitCore(); + Result result= core.run(InfiniteLoopTest.class); + assertEquals(1, result.getRunCount()); + assertEquals(1, result.getFailureCount()); + Throwable exception= result.getFailures().get(0).getException(); + Writer buffer= new StringWriter(); + PrintWriter writer= new PrintWriter(buffer); + exception.printStackTrace(writer); + assertThat(buffer.toString(), containsString("infiniteLoop")); // Make sure we have the stalled frame on the stack somewhere + } + + @Test public void compatibility() { + TestResult result= new TestResult(); + new JUnit4TestAdapter(InfiniteLoopTest.class).run(result); + assertEquals(1, result.errorCount()); + } + + public static class WillTimeOut { + static boolean afterWasCalled= false; + + @Test(timeout=1) public void test() { + for(;;) + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + // ok, tests are over + } + } + + @After public void after() { + afterWasCalled= true; + } + } + + @Test public void makeSureAfterIsCalledAfterATimeout() { + JUnitCore.runClasses(WillTimeOut.class); + assertThat(WillTimeOut.afterWasCalled, is(true)); + } +} diff --git a/junit4/src/test/java/org/junit/tests/validation/BadlyFormedClassesTest.java b/junit4/src/test/java/org/junit/tests/validation/BadlyFormedClassesTest.java new file mode 100644 index 0000000..ff61822 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/validation/BadlyFormedClassesTest.java @@ -0,0 +1,68 @@ +package org.junit.tests.validation; + +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import org.junit.internal.runners.JUnit4ClassRunner; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; + +@SuppressWarnings("deprecation") +public class BadlyFormedClassesTest { + public static class FaultyConstructor { + public FaultyConstructor() throws Exception { + throw new Exception("Thrown during construction"); + } + + @Test + public void someTest() { + /* + * Empty test just to fool JUnit and IDEs into running this class as + * a JUnit test + */ + } + }; + + @RunWith(JUnit4ClassRunner.class) + public static class BadBeforeMethodWithLegacyRunner { + @Before + void before() { + + } + + @Test + public void someTest() { + } + }; + + public static class NoTests { + // class without tests + } + + @Test + public void constructorException() { + String message= exceptionMessageFrom(FaultyConstructor.class); + assertEquals("Thrown during construction", message); + } + + @Test + public void noRunnableMethods() { + assertEquals("No runnable methods", exceptionMessageFrom(NoTests.class)); + } + + @Test + public void badBeforeMethodWithLegacyRunner() { + assertEquals("Method before should be public", + exceptionMessageFrom(BadBeforeMethodWithLegacyRunner.class)); + } + + private String exceptionMessageFrom(Class<?> testClass) { + JUnitCore core= new JUnitCore(); + Result result= core.run(testClass); + Failure failure= result.getFailures().get(0); + String message= failure.getException().getMessage(); + return message; + } +} diff --git a/junit4/src/test/java/org/junit/tests/validation/FailedConstructionTest.java b/junit4/src/test/java/org/junit/tests/validation/FailedConstructionTest.java new file mode 100644 index 0000000..7d2ab1e --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/validation/FailedConstructionTest.java @@ -0,0 +1,28 @@ +package org.junit.tests.validation; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; + +public class FailedConstructionTest { + public static class CantConstruct { + public CantConstruct() { + throw new RuntimeException(); + } + + @Test + public void foo() { + } + } + + @Test + public void failedConstructionIsTestFailure() { + Result result= JUnitCore.runClasses(CantConstruct.class); + Failure failure= result.getFailures().get(0); + Description expected= Description.createTestDescription(CantConstruct.class, "foo"); + Assert.assertEquals(expected, failure.getDescription()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/validation/InaccessibleBaseClassTest.java b/junit4/src/test/java/org/junit/tests/validation/InaccessibleBaseClassTest.java new file mode 100644 index 0000000..0677dd8 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/validation/InaccessibleBaseClassTest.java @@ -0,0 +1,13 @@ +package org.junit.tests.validation; + +import org.junit.Test; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; +import org.junit.tests.validation.anotherpackage.Sub; + +public class InaccessibleBaseClassTest { + @Test(expected= InitializationError.class) + public void inaccessibleBaseClassIsCaughtAtValidation() throws InitializationError { + new BlockJUnit4ClassRunner(Sub.class); + } +} diff --git a/junit4/src/test/java/org/junit/tests/validation/ValidationTest.java b/junit4/src/test/java/org/junit/tests/validation/ValidationTest.java new file mode 100644 index 0000000..3251705 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/validation/ValidationTest.java @@ -0,0 +1,34 @@ +package org.junit.tests.validation; + +import static org.junit.Assert.assertEquals; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.Result; + +public class ValidationTest { + public static class WrongBeforeClass { + @BeforeClass + protected int a() { + return 0; + } + } + + @Test + public void initializationErrorIsOnCorrectClass() { + assertEquals(WrongBeforeClass.class.getName(), + Request.aClass(WrongBeforeClass.class).getRunner().getDescription().getDisplayName()); + } + + public static class NonStaticBeforeClass { + @BeforeClass public void before() {} + @Test public void hereBecauseEveryTestClassNeedsATest() {} + } + + @Test + public void nonStaticBeforeClass() { + Result result= JUnitCore.runClasses(NonStaticBeforeClass.class); + assertEquals("Method before() should be static", result.getFailures().get(0).getMessage()); + } +} diff --git a/junit4/src/test/java/org/junit/tests/validation/anotherpackage/Sub.java b/junit4/src/test/java/org/junit/tests/validation/anotherpackage/Sub.java new file mode 100644 index 0000000..44d6c76 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/validation/anotherpackage/Sub.java @@ -0,0 +1,5 @@ +package org.junit.tests.validation.anotherpackage; + +public class Sub extends Super { + +} diff --git a/junit4/src/test/java/org/junit/tests/validation/anotherpackage/Super.java b/junit4/src/test/java/org/junit/tests/validation/anotherpackage/Super.java new file mode 100644 index 0000000..ba1cfe4 --- /dev/null +++ b/junit4/src/test/java/org/junit/tests/validation/anotherpackage/Super.java @@ -0,0 +1,7 @@ +package org.junit.tests.validation.anotherpackage; + +import org.junit.Test; + +class Super { + @Test public void a() {} +} |