diff --git a/acknowledgements.txt b/acknowledgements.txt index 0d70914839af..4d7ee6c40655 100644 --- a/acknowledgements.txt +++ b/acknowledgements.txt @@ -104,9 +104,18 @@ Samuel Le Berrigaud (sleberrigaud@github): Report for GH-248: protected BlockJUnit4ClassRunner#rules method removed from 4.8.2 +2011 Jun 24 + Daniel Rothmaler (drothmaler@github): + #299: random temp file/folder creation + #300: ErrorCollector.checkThat overload + 2011 Jul 06 Stefan Birkner: Fixed wrong documentation of ClassRule (github#254). +2011 Jul 08 + Paul Holser (pholser@alumni.rice.edu): Beginnings of fix for GH-64: + Theories doesn't honor parameterized types + 2011 Jul 09 Nigel Charman: Reported Rules bugs github#257 and gihub#258. @@ -116,11 +125,35 @@ 2011 Jul 09 Stefan Birkner: Fixed rules bugs (github#257, gihub#258, github#260). +2011 Jul 16 + Rob Dawson: Submitted a patch that makes Results serlializable. + +2011 Jul 20 + Asaf Ary, Stefan Birkner: Fixed FailOnTimeout class (github#265). + 2011 Jul 22 Andreas Köhler, Stefan Birkner: Fixed wrong documentation of Parameterized (github#89). 2011 Jul 28 electrickery, Stefan Birkner: Fixed typo in JavaDoc (github#134). +2011 Aug 07 + Esko Luontola: Fixed TemporaryFolder creating files in the current working directory (github#278). + 2011 Aug 09 Stefan Birkner: Fixed JavaDoc links. + +2011 Aug 10 + rodolfoliviero@github and JoseRibeiro@github: feature to create recursive temporary folders. + +2011 Aug 12 + Esko Luontola: Fixed syntax error in Parameterized's usage example (github#285). + +2011 Sep 09 + Robert Munteanu, Stefan Birkner: + TestWatcher and TestWatchman don't call failed when assumption is violated (github#296). + digulla@github, Stefan Birkner: Removed useless code (github#289). + +== NOTE: as of September 2011, we have stopped recording contributions here. + For a full list of everyone who has contributed great bug reports and code, please see + http://github.com/KentBeck/junit diff --git a/build.xml b/build.xml index 6e2e8675c10b..26f3275f7452 100644 --- a/build.xml +++ b/build.xml @@ -7,7 +7,7 @@ - + diff --git a/doc/ReleaseNotes4.10.html b/doc/ReleaseNotes4.10.html new file mode 100644 index 000000000000..eb6a53668726 --- /dev/null +++ b/doc/ReleaseNotes4.10.html @@ -0,0 +1,18 @@ +

Summary of Changes in version 4.10 [unreleased!]

+ +

Bug fixes

+ +

Minor changes

+ +

Thanks to @rodolfoliviero for:

+ +
    +
  • github#283: Feature to create recursive temporary folders.
  • +
+ +

Thanks to @drothmaler for:

+ +
    +
  • github#299: Random temporary file/folder creation
  • +
  • github#300: New ErrorCollector.checkThat overload, that allows you to specify a reason
  • +
diff --git a/doc/ReleaseNotes4.10.txt b/doc/ReleaseNotes4.10.txt new file mode 100644 index 000000000000..d8f7d9b322cd --- /dev/null +++ b/doc/ReleaseNotes4.10.txt @@ -0,0 +1,14 @@ +## Summary of Changes in version 4.10 [unreleased!] ## + +### Bug fixes ### + +### Minor changes ### + +Thanks to `@rodolfoliviero` for: + +- github#283: Feature to create recursive temporary folders. + +Thanks to `@drothmaler` for: + +- github#299: Random temporary file/folder creation +- github#300: New `ErrorCollector.checkThat` overload, that allows you to specify a reason diff --git a/doc/ReleaseNotes4.9.1.txt b/doc/ReleaseNotes4.9.1.txt new file mode 100644 index 000000000000..eded783dbb66 --- /dev/null +++ b/doc/ReleaseNotes4.9.1.txt @@ -0,0 +1,14 @@ +## Summary of Changes in version 4.9.1 [unreleased!] ## + +### Theories ### + +The `Theories` runner does not anticipate theory parameters that have generic +types, as reported by github#64. Fixing this won't happen until `Theories` is +moved to junit-contrib. In anticipation of this, 4.9.1 adds some of the +necessary machinery to the runner classes, and deprecates a method that only +the `Theories` runner uses, `FrameworkMethod`#producesType(). +The Common Public License that JUnit is released under is now included +in the source repository. + +Thanks to `@pholser` for identifying a potential resolution for github#64 +and initiating work on it. diff --git a/src/main/java/junit/runner/Version.java b/src/main/java/junit/runner/Version.java index 01db4a37b3b2..21aabfa12079 100644 --- a/src/main/java/junit/runner/Version.java +++ b/src/main/java/junit/runner/Version.java @@ -9,7 +9,7 @@ private Version() { } public static String id() { - return "4.9.1-SNAPSHOT"; + return "4.10-SNAPSHOT"; } public static void main(String[] args) { diff --git a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java index 8f5d77a7dcf7..bff7c7235107 100644 --- a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java +++ b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java @@ -6,41 +6,66 @@ import org.junit.runners.model.Statement; public class FailOnTimeout extends Statement { - private Statement fNext; + private final Statement fOriginalStatement; private final long fTimeout; - private boolean fFinished= false; - - private Throwable fThrown= null; - - public FailOnTimeout(Statement next, long timeout) { - fNext= next; + public FailOnTimeout(Statement originalStatement, long timeout) { + fOriginalStatement= originalStatement; fTimeout= timeout; } @Override public void evaluate() throws Throwable { - Thread thread= new Thread() { - @Override - public void run() { - try { - fNext.evaluate(); - fFinished= true; - } catch (Throwable e) { - fThrown= e; - } - } - }; + StatementThread thread= evaluateStatement(); + if (!thread.fFinished) + throwExceptionForUnfinishedThread(thread); + } + + private StatementThread evaluateStatement() throws InterruptedException { + StatementThread thread= new StatementThread(fOriginalStatement); thread.start(); thread.join(fTimeout); - if (fFinished) - return; - if (fThrown != null) - throw fThrown; + 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/src/main/java/org/junit/internal/runners/statements/RunAfters.java b/src/main/java/org/junit/internal/runners/statements/RunAfters.java index 3f9573a745c2..475ec7261684 100644 --- a/src/main/java/org/junit/internal/runners/statements/RunAfters.java +++ b/src/main/java/org/junit/internal/runners/statements/RunAfters.java @@ -26,7 +26,6 @@ public RunAfters(Statement next, List afters, Object target) { @Override public void evaluate() throws Throwable { List errors = new ArrayList(); - errors.clear(); try { fNext.evaluate(); } catch (Throwable e) { diff --git a/src/main/java/org/junit/rules/ErrorCollector.java b/src/main/java/org/junit/rules/ErrorCollector.java index b48d99d84c12..3522a654f415 100644 --- a/src/main/java/org/junit/rules/ErrorCollector.java +++ b/src/main/java/org/junit/rules/ErrorCollector.java @@ -52,18 +52,27 @@ public void addError(Throwable error) { * Execution continues, but the test will fail at the end if the match fails. */ public void checkThat(final T value, final Matcher 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 void checkThat(final String reason, final T value, final Matcher matcher) { checkSucceeds(new Callable() { public Object call() throws Exception { - assertThat(value, matcher); + 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. + * 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 callable) { try { @@ -71,6 +80,6 @@ public Object checkSucceeds(Callable callable) { } catch (Throwable e) { addError(e); return null; - } + } } } \ No newline at end of file diff --git a/src/main/java/org/junit/rules/TemporaryFolder.java b/src/main/java/org/junit/rules/TemporaryFolder.java index fb8941eb20b3..a7c82aab5246 100644 --- a/src/main/java/org/junit/rules/TemporaryFolder.java +++ b/src/main/java/org/junit/rules/TemporaryFolder.java @@ -42,39 +42,61 @@ protected void after() { * for testing purposes only. Do not use. */ public void create() throws IOException { - folder= File.createTempFile("junit", ""); - folder.delete(); - folder.mkdir(); + 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(folder, fileName); + 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 folderName) { - File file= new File(folder, folderName); - file.mkdir(); + 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 + * Usually not called directly, since it is automatically applied * by the {@link Rule} */ public void delete() { @@ -88,4 +110,4 @@ private void recursiveDelete(File file) { recursiveDelete(each); file.delete(); } -} \ No newline at end of file +} diff --git a/src/main/java/org/junit/rules/TestWatcher.java b/src/main/java/org/junit/rules/TestWatcher.java index 35603adeaf24..351b449e522d 100644 --- a/src/main/java/org/junit/rules/TestWatcher.java +++ b/src/main/java/org/junit/rules/TestWatcher.java @@ -1,5 +1,6 @@ package org.junit.rules; +import org.junit.internal.AssumptionViolatedException; import org.junit.runner.Description; import org.junit.runners.model.Statement; @@ -45,6 +46,8 @@ public void evaluate() throws Throwable { try { base.evaluate(); succeeded(description); + } catch (AssumptionViolatedException e) { + throw e; } catch (Throwable t) { failed(t, description); throw t; diff --git a/src/main/java/org/junit/rules/TestWatchman.java b/src/main/java/org/junit/rules/TestWatchman.java index 6ebd45f84bd3..15daa6419373 100644 --- a/src/main/java/org/junit/rules/TestWatchman.java +++ b/src/main/java/org/junit/rules/TestWatchman.java @@ -1,5 +1,6 @@ package org.junit.rules; +import org.junit.internal.AssumptionViolatedException; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; @@ -51,6 +52,8 @@ public void evaluate() throws Throwable { try { base.evaluate(); succeeded(method); + } catch (AssumptionViolatedException e) { + throw e; } catch (Throwable t) { failed(t, method); throw t; diff --git a/src/main/java/org/junit/runner/Description.java b/src/main/java/org/junit/runner/Description.java index 7bba883248e6..b3083d5e05a7 100644 --- a/src/main/java/org/junit/runner/Description.java +++ b/src/main/java/org/junit/runner/Description.java @@ -1,5 +1,6 @@ package org.junit.runner; +import java.io.Serializable; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; @@ -24,8 +25,9 @@ * @see org.junit.runner.Request * @see org.junit.runner.Runner */ -public class Description { - +public class Description implements Serializable { + private static final long serialVersionUID = 1L; + /** * Create a Description named name. * Generally, you will add children to this Description. diff --git a/src/main/java/org/junit/runner/Result.java b/src/main/java/org/junit/runner/Result.java index de5cf02784e4..edfb97c543f7 100644 --- a/src/main/java/org/junit/runner/Result.java +++ b/src/main/java/org/junit/runner/Result.java @@ -1,5 +1,6 @@ package org.junit.runner; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Collections; @@ -13,7 +14,8 @@ * tests. Since tests are expected to run correctly, successful tests are only noted in * the count of tests that ran. */ -public class Result { +public class Result implements Serializable { + private static final long serialVersionUID = 1L; private AtomicInteger fCount = new AtomicInteger(); private AtomicInteger fIgnoreCount= new AtomicInteger(); private final List fFailures= Collections.synchronizedList(new ArrayList()); diff --git a/src/main/java/org/junit/runner/notification/Failure.java b/src/main/java/org/junit/runner/notification/Failure.java index 6a2d1a268394..501caa5ed1e0 100644 --- a/src/main/java/org/junit/runner/notification/Failure.java +++ b/src/main/java/org/junit/runner/notification/Failure.java @@ -1,6 +1,7 @@ package org.junit.runner.notification; import java.io.PrintWriter; +import java.io.Serializable; import java.io.StringWriter; import org.junit.runner.Description; @@ -12,7 +13,8 @@ * 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 { +public class Failure implements Serializable { + private static final long serialVersionUID = 1L; private final Description fDescription; private final Throwable fThrownException; diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java index a0ed9f11a1f8..c006cd6b3a60 100644 --- a/src/main/java/org/junit/runners/Parameterized.java +++ b/src/main/java/org/junit/runners/Parameterized.java @@ -31,8 +31,8 @@ * @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 } } }); + * { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } + * }); * } * * private int fInput; diff --git a/src/main/java/org/junit/runners/model/FrameworkMethod.java b/src/main/java/org/junit/runners/model/FrameworkMethod.java index 8d709ba8486d..81c89634c7fd 100644 --- a/src/main/java/org/junit/runners/model/FrameworkMethod.java +++ b/src/main/java/org/junit/runners/model/FrameworkMethod.java @@ -4,6 +4,7 @@ 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; @@ -90,6 +91,10 @@ public void validatePublicVoid(boolean isStatic, List errors) { errors.add(new Exception("Method " + fMethod.getName() + "() should be void")); } + public void validateNoTypeParametersOnArgs(List errors) { + new NoGenericTypeParametersValidator(fMethod).validate(errors); + } + @Override public boolean isShadowedBy(FrameworkMethod other) { if (!other.getName().equals(getName())) @@ -117,10 +122,16 @@ public int 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. */ - public boolean producesType(Class type) { - return getParameterTypes().length == 0 - && type.isAssignableFrom(fMethod.getReturnType()); + @Deprecated + public boolean producesType(Type type) { + return getParameterTypes().length == 0 && type instanceof Class + && ((Class) type).isAssignableFrom(fMethod.getReturnType()); } private Class[] getParameterTypes() { @@ -142,4 +153,4 @@ public Annotation[] getAnnotations() { public T getAnnotation(Class annotationType) { return fMethod.getAnnotation(annotationType); } -} \ No newline at end of file +} diff --git a/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java b/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java new file mode 100644 index 000000000000..77662b8cd124 --- /dev/null +++ b/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 errors) { + for (Type each : fMethod.getGenericParameterTypes()) + validateNoTypeParameterOnType(each, errors); + } + + private void validateNoTypeParameterOnType(Type type, List 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 errors) { + for (Type each : parameterized.getActualTypeArguments()) + validateNoTypeParameterOnType(each, errors); + } + + private void validateNoTypeParameterOnWildcardType(WildcardType wildcard, + List errors) { + for (Type each : wildcard.getUpperBounds()) + validateNoTypeParameterOnType(each, errors); + for (Type each : wildcard.getLowerBounds()) + validateNoTypeParameterOnType(each, errors); + } + + private void validateNoTypeParameterOnGenericArrayType( + GenericArrayType arrayType, List errors) { + validateNoTypeParameterOnType(arrayType.getGenericComponentType(), errors); + } +} \ No newline at end of file diff --git a/src/test/java/junit/tests/runner/AllTests.java b/src/test/java/junit/tests/runner/AllTests.java index 62001ddf2aa6..a0aa11668e21 100644 --- a/src/test/java/junit/tests/runner/AllTests.java +++ b/src/test/java/junit/tests/runner/AllTests.java @@ -16,6 +16,7 @@ public static void main(String[] args) { 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); @@ -27,4 +28,4 @@ static boolean isJDK11() { String version= System.getProperty("java.version"); return version.startsWith("1.1"); } -} \ No newline at end of file +} diff --git a/src/test/java/junit/tests/runner/ResultTest.java b/src/test/java/junit/tests/runner/ResultTest.java new file mode 100644 index 000000000000..ba3b509b3abb --- /dev/null +++ b/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/src/test/java/org/junit/tests/experimental/rules/LoggingTestWatcher.java b/src/test/java/org/junit/tests/experimental/rules/LoggingTestWatcher.java new file mode 100644 index 000000000000..98251b96aa06 --- /dev/null +++ b/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/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java b/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java index d5487fc00d3e..03393a1a55f1 100644 --- a/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java +++ b/src/test/java/org/junit/tests/experimental/rules/TempFolderRuleTest.java @@ -1,20 +1,25 @@ 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 createdFile; + private static File[] createdFiles= new File[20]; public static class HasTempFolder { @Rule @@ -22,15 +27,15 @@ public static class HasTempFolder { @Test public void testUsingTempFolder() throws IOException { - createdFile= folder.newFile("myfile.txt"); - assertTrue(createdFile.exists()); + createdFiles[0]= folder.newFile("myfile.txt"); + assertTrue(createdFiles[0].exists()); } } @Test public void tempFolderIsDeleted() { assertThat(testResult(HasTempFolder.class), isSuccessful()); - assertFalse(createdFile.exists()); + assertFalse(createdFiles[0].exists()); } public static class CreatesSubFolder { @@ -39,16 +44,90 @@ public static class CreatesSubFolder { @Test public void testUsingTempFolder() throws IOException { - createdFile= folder.newFolder("subfolder"); - new File(createdFile, "a.txt").createNewFile(); - assertTrue(createdFile.exists()); + 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(createdFile.exists()); + 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 @@ -61,6 +140,16 @@ public void recursiveDeleteFolderWithOneElement() throws IOException { 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(); @@ -68,4 +157,44 @@ public void recursiveDeleteFolderWithZeroElements() throws IOException { 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() throws IOException { + 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/src/test/java/org/junit/tests/experimental/rules/TestRuleTest.java b/src/test/java/org/junit/tests/experimental/rules/TestRuleTest.java index c469e84703fb..167eb201beb4 100644 --- a/src/test/java/org/junit/tests/experimental/rules/TestRuleTest.java +++ b/src/test/java/org/junit/tests/experimental/rules/TestRuleTest.java @@ -146,7 +146,7 @@ public void ignoreNonRules() { public static class OnFailureTest { @Rule - public TestRule watchman= new TestWatcher() { + public TestRule watcher= new TestWatcher() { @Override protected void failed(Throwable e, Description description) { log+= description + " " + e.getClass().getSimpleName(); @@ -171,7 +171,7 @@ public static class WatchmanTest { private static String watchedLog; @Rule - public TestRule watchman= new TestWatcher() { + public TestRule watcher= new TestWatcher() { @Override protected void failed(Throwable e, Description description) { watchedLog+= description + " " @@ -203,45 +203,33 @@ public void succeeded() { } public static class BeforesAndAfters { - private static String watchedLog; + private static StringBuilder watchedLog= new StringBuilder(); - @Before public void before() { - watchedLog+= "before "; + @Before + public void before() { + watchedLog.append("before "); } @Rule - public TestRule watchman= new TestWatcher() { - @Override - protected void starting(Description d) { - watchedLog+= "starting "; - } - - @Override - protected void finished(Description d) { - watchedLog+= "finished "; - } - - @Override - protected void succeeded(Description d) { - watchedLog+= "succeeded "; - } - }; + public TestRule watcher= new LoggingTestWatcher(watchedLog); - @After public void after() { - watchedLog+= "after "; + @After + public void after() { + watchedLog.append("after "); } @Test public void succeeds() { - watchedLog+= "test "; + watchedLog.append("test "); } } @Test public void beforesAndAfters() { - BeforesAndAfters.watchedLog= ""; + BeforesAndAfters.watchedLog= new StringBuilder(); JUnitCore.runClasses(BeforesAndAfters.class); - assertThat(BeforesAndAfters.watchedLog, is("starting before test after succeeded finished ")); + assertThat(BeforesAndAfters.watchedLog.toString(), + is("starting before test after succeeded finished ")); } public static class WrongTypedField { @@ -298,4 +286,4 @@ public static class UsesCustomMethodRule { @Test public void useCustomMethodRule() { assertThat(testResult(UsesCustomMethodRule.class), isSuccessful()); } -} +} \ No newline at end of file diff --git a/src/test/java/org/junit/tests/experimental/rules/TestWatcherTest.java b/src/test/java/org/junit/tests/experimental/rules/TestWatcherTest.java new file mode 100644 index 000000000000..19ac3e6aea1e --- /dev/null +++ b/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/src/test/java/org/junit/tests/experimental/rules/TestWatchmanTest.java b/src/test/java/org/junit/tests/experimental/rules/TestWatchmanTest.java new file mode 100644 index 000000000000..0296a1eb371c --- /dev/null +++ b/src/test/java/org/junit/tests/experimental/rules/TestWatchmanTest.java @@ -0,0 +1,71 @@ +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; + +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/src/test/java/org/junit/tests/experimental/rules/VerifierRuleTest.java b/src/test/java/org/junit/tests/experimental/rules/VerifierRuleTest.java index 2c24fd713868..95e8ec3c4a73 100644 --- a/src/test/java/org/junit/tests/experimental/rules/VerifierRuleTest.java +++ b/src/test/java/org/junit/tests/experimental/rules/VerifierRuleTest.java @@ -52,6 +52,8 @@ public static class UsesErrorCollectorCheckThat { @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)); } } @@ -59,6 +61,10 @@ public static class UsesErrorCollectorCheckThat { 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 { diff --git a/src/test/java/org/junit/tests/experimental/theories/runner/WithUnresolvedGenericTypeVariablesOnTheoryParms.java b/src/test/java/org/junit/tests/experimental/theories/runner/WithUnresolvedGenericTypeVariablesOnTheoryParms.java new file mode 100644 index 000000000000..579fc7e343dd --- /dev/null +++ b/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 strings = Arrays.asList("foo", "bar"); + + @Theory + public 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 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 strings = Arrays.asList("foo", "bar"); + + @Theory + public void forItems(Collection 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 strings = Arrays.asList("foo", "bar"); + + @Theory + public void forItems(Collection 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 strings = Arrays.asList("foo", "bar"); + + @Theory + public void forItems(Collection 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 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 void forItems(Collection[] items) { + } + } + + @Test + public void whereTypeVariableIsOnTheoryClass() { + PrintableResult result= testResult(TypeVariableOnTheoryClass.class); + assertThat(result, hasSingleFailureContaining("unresolved type variable T")); + } + + @RunWith(Theories.class) + public static class TypeVariableOnTheoryClass { + @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> { + @Theory + public void forItem(A first, Collection second, + Map third, List fourth, F[] fifth, + Collection[] sixth) { + } + } +} \ No newline at end of file diff --git a/src/test/java/org/junit/tests/internal/runners/statements/FailOnTimeoutTest.java b/src/test/java/org/junit/tests/internal/runners/statements/FailOnTimeoutTest.java new file mode 100644 index 000000000000..152d64139bb5 --- /dev/null +++ b/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