aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/junit/rules
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/junit/rules')
-rw-r--r--src/main/java/org/junit/rules/ErrorCollector.java42
-rw-r--r--src/main/java/org/junit/rules/ExpectedException.java57
-rw-r--r--src/main/java/org/junit/rules/ExternalResource.java15
-rw-r--r--src/main/java/org/junit/rules/MethodRule.java16
-rw-r--r--src/main/java/org/junit/rules/RuleChain.java50
-rw-r--r--src/main/java/org/junit/rules/Stopwatch.java2
-rw-r--r--src/main/java/org/junit/rules/TemporaryFolder.java277
-rw-r--r--src/main/java/org/junit/rules/TestName.java2
-rw-r--r--src/main/java/org/junit/rules/TestWatcher.java12
-rw-r--r--src/main/java/org/junit/rules/Timeout.java4
10 files changed, 368 insertions, 109 deletions
diff --git a/src/main/java/org/junit/rules/ErrorCollector.java b/src/main/java/org/junit/rules/ErrorCollector.java
index 8c6600e..18d94b8 100644
--- a/src/main/java/org/junit/rules/ErrorCollector.java
+++ b/src/main/java/org/junit/rules/ErrorCollector.java
@@ -1,11 +1,14 @@
package org.junit.rules;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertThrows;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
+import org.junit.function.ThrowingRunnable;
+import org.junit.internal.AssumptionViolatedException;
import org.hamcrest.Matcher;
import org.junit.runners.model.MultipleFailureException;
@@ -43,6 +46,21 @@ public class ErrorCollector extends Verifier {
* Adds a Throwable to the table. Execution continues, but the test will fail at the end.
*/
public void addError(Throwable error) {
+ if (error == null) {
+ throw new NullPointerException("Error cannot be null");
+ }
+ // BEGIN Android-changed: Don't convert assumption failures to errors. b/181123057
+ // Submitted upstream: https://github.com/junit-team/junit4/issues/1703
+ /*
+ if (error instanceof AssumptionViolatedException) {
+ AssertionError e = new AssertionError(error.getMessage());
+ e.initCause(error);
+ errors.add(e);
+ } else {
+ errors.add(error);
+ }
+ */
+ // END Android-changed: Don't convert assumption failures to errors. b/181123057
errors.add(error);
}
@@ -76,9 +94,33 @@ public class ErrorCollector extends Verifier {
public <T> T checkSucceeds(Callable<T> callable) {
try {
return callable.call();
+ } catch (AssumptionViolatedException e) {
+ AssertionError error = new AssertionError("Callable threw AssumptionViolatedException");
+ error.initCause(e);
+ addError(error);
+ return null;
} catch (Throwable e) {
addError(e);
return null;
}
}
+
+ /**
+ * Adds a failure to the table if {@code runnable} does not throw an
+ * exception of type {@code expectedThrowable} when executed.
+ * Execution continues, but the test will fail at the end if the runnable
+ * does not throw an exception, or if it throws a different exception.
+ *
+ * @param expectedThrowable the expected type of the exception
+ * @param runnable a function that is expected to throw an exception when executed
+ * @since 4.13
+ */
+ public void checkThrows(Class<? extends Throwable> expectedThrowable, ThrowingRunnable runnable) {
+ try {
+ assertThrows(expectedThrowable, runnable);
+ } catch (AssertionError e) {
+ addError(e);
+ }
+ }
+
}
diff --git a/src/main/java/org/junit/rules/ExpectedException.java b/src/main/java/org/junit/rules/ExpectedException.java
index 4d61712..431ad49 100644
--- a/src/main/java/org/junit/rules/ExpectedException.java
+++ b/src/main/java/org/junit/rules/ExpectedException.java
@@ -7,7 +7,6 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause;
import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
-
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.junit.AssumptionViolatedException;
@@ -21,7 +20,7 @@ import org.junit.runners.model.Statement;
*
* <pre> public class SimpleExpectedExceptionTest {
* &#064;Rule
- * public ExpectedException thrown= ExpectedException.none();
+ * public ExpectedException thrown = ExpectedException.none();
*
* &#064;Test
* public void throwsNothing() {
@@ -35,16 +34,19 @@ import org.junit.runners.model.Statement;
* }
* }</pre>
*
- * <p>
- * You have to add the {@code ExpectedException} rule to your test.
+ * <p>You have to add the {@code ExpectedException} rule to your test.
* This doesn't affect your existing tests (see {@code throwsNothing()}).
- * After specifiying the type of the expected exception your test is
+ * After specifying the type of the expected exception your test is
* successful when such an exception is thrown and it fails if a
* different or no exception is thrown.
*
- * <p>
- * Instead of specifying the exception's type you can characterize the
- * expected exception based on other criterias, too:
+ * <p>This rule does not perform any special magic to make execution continue
+ * as if the exception had not been thrown. So it is nearly always a mistake
+ * for a test method to have statements after the one that is expected to
+ * throw the exception.
+ *
+ * <p>Instead of specifying the exception's type you can characterize the
+ * expected exception based on other criteria, too:
*
* <ul>
* <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li>
@@ -53,8 +55,7 @@ import org.junit.runners.model.Statement;
* <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li>
* </ul>
*
- * <p>
- * You can combine any of the presented expect-methods. The test is
+ * <p>You can combine any of the presented expect-methods. The test is
* successful if all specifications are met.
* <pre> &#064;Test
* public void throwsException() {
@@ -63,9 +64,15 @@ import org.junit.runners.model.Statement;
* throw new NullPointerException(&quot;What happened?&quot;);
* }</pre>
*
+ * <p>It is recommended to set the {@link org.junit.Rule#order() order} of the
+ * {@code ExpectedException} to {@code Integer.MAX_VALUE} if it is used together
+ * with another rule that handles exceptions, e.g. {@link ErrorCollector}.
+ * Otherwise failing tests may be successful.
+ * <pre> &#064;Rule(order = Integer.MAX_VALUE)
+ * public ExpectedException thrown = ExpectedException.none();</pre>
+ *
* <h3>AssumptionViolatedExceptions</h3>
- * <p>
- * JUnit uses {@link AssumptionViolatedException}s for indicating that a test
+ * <p>JUnit uses {@link AssumptionViolatedException}s for indicating that a test
* provides no useful information. (See {@link org.junit.Assume} for more
* information.) You have to call {@code assume} methods before you set
* expectations of the {@code ExpectedException} rule. In this case the rule
@@ -80,8 +87,7 @@ import org.junit.runners.model.Statement;
*
* <h3>AssertionErrors</h3>
*
- * <p>
- * JUnit uses {@link AssertionError}s for indicating that a test is failing. You
+ * <p>JUnit uses {@link AssertionError}s for indicating that a test is failing. You
* have to call {@code assert} methods before you set expectations of the
* {@code ExpectedException} rule, if they should be handled by the framework.
* E.g. the following test fails because of the {@code assertTrue} statement.
@@ -93,8 +99,7 @@ import org.junit.runners.model.Statement;
* }</pre>
*
* <h3>Missing Exceptions</h3>
- * <p>
- * By default missing exceptions are reported with an error message
+ * <p>By default missing exceptions are reported with an error message
* like "Expected test to throw an instance of foo". You can configure a different
* message by means of {@link #reportMissingExceptionWithMessage(String)}. You
* can use a {@code %s} placeholder for the description of the expected
@@ -107,7 +112,13 @@ public class ExpectedException implements TestRule {
/**
* Returns a {@linkplain TestRule rule} that expects no exception to
* be thrown (identical to behavior without this rule).
+ *
+ * @deprecated Since 4.13
+ * {@link org.junit.Assert#assertThrows(Class, org.junit.function.ThrowingRunnable)
+ * Assert.assertThrows} can be used to verify that your code throws a specific
+ * exception.
*/
+ @Deprecated
public static ExpectedException none() {
return new ExpectedException();
}
@@ -222,10 +233,18 @@ public class ExpectedException implements TestRule {
* throw new IllegalArgumentException(&quot;What happened?&quot;, cause);
* }</pre>
*/
- public void expectCause(Matcher<? extends Throwable> expectedCause) {
+ public void expectCause(Matcher<?> expectedCause) {
expect(hasCause(expectedCause));
}
+ /**
+ * Check if any Exception is expected.
+ * @since 4.13
+ */
+ public final boolean isAnyExceptionExpected() {
+ return matcherBuilder.expectsThrowable();
+ }
+
private class ExpectedExceptionStatement extends Statement {
private final Statement next;
@@ -255,10 +274,6 @@ public class ExpectedException implements TestRule {
}
}
- private boolean isAnyExceptionExpected() {
- return matcherBuilder.expectsThrowable();
- }
-
private void failDueToMissingException() throws AssertionError {
fail(missingExceptionMessage());
}
diff --git a/src/main/java/org/junit/rules/ExternalResource.java b/src/main/java/org/junit/rules/ExternalResource.java
index 71ca287..71fc842 100644
--- a/src/main/java/org/junit/rules/ExternalResource.java
+++ b/src/main/java/org/junit/rules/ExternalResource.java
@@ -1,6 +1,10 @@
package org.junit.rules;
+import java.util.ArrayList;
+import java.util.List;
+
import org.junit.runner.Description;
+import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
/**
@@ -44,11 +48,20 @@ public abstract class ExternalResource implements TestRule {
@Override
public void evaluate() throws Throwable {
before();
+
+ List<Throwable> errors = new ArrayList<Throwable>();
try {
base.evaluate();
+ } catch (Throwable t) {
+ errors.add(t);
} finally {
- after();
+ try {
+ after();
+ } catch (Throwable t) {
+ errors.add(t);
+ }
}
+ MultipleFailureException.assertEmpty(errors);
}
};
}
diff --git a/src/main/java/org/junit/rules/MethodRule.java b/src/main/java/org/junit/rules/MethodRule.java
index 823ee78..94608f5 100644
--- a/src/main/java/org/junit/rules/MethodRule.java
+++ b/src/main/java/org/junit/rules/MethodRule.java
@@ -10,21 +10,9 @@ import org.junit.runners.model.Statement;
* {@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:
+ * an example of how this can be useful, see {@link TestWatchman}.
*
- * <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>
- *
- * Note that {@link MethodRule} has been replaced by {@link TestRule},
+ * <p>Note that {@link MethodRule} has been replaced by {@link TestRule},
* which has the added benefit of supporting class rules.
*
* @since 4.7
diff --git a/src/main/java/org/junit/rules/RuleChain.java b/src/main/java/org/junit/rules/RuleChain.java
index f43d8f5..bf93aae 100644
--- a/src/main/java/org/junit/rules/RuleChain.java
+++ b/src/main/java/org/junit/rules/RuleChain.java
@@ -4,26 +4,34 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.junit.Rule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
- * The RuleChain rule allows ordering of TestRules. You create a
+ * The {@code RuleChain} can be used for creating composite rules. You create a
* {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of
* {@link #around(TestRule)}:
*
* <pre>
- * public static class UseRuleChain {
- * &#064;Rule
- * public RuleChain chain= RuleChain
- * .outerRule(new LoggingRule("outer rule")
- * .around(new LoggingRule("middle rule")
- * .around(new LoggingRule("inner rule");
+ * public abstract class CompositeRules {
+ * public static TestRule extendedLogging() {
+ * return RuleChain.outerRule(new LoggingRule("outer rule"))
+ * .around(new LoggingRule("middle rule"))
+ * .around(new LoggingRule("inner rule"));
+ * }
+ * }
+ * </pre>
+ *
+ * <pre>
+ * public class UseRuleChain {
+ * &#064;Rule
+ * public final TestRule extendedLogging = CompositeRules.extendedLogging();
*
- * &#064;Test
- * public void example() {
- * assertTrue(true);
- * }
+ * &#064;Test
+ * public void example() {
+ * assertTrue(true);
+ * }
* }
* </pre>
*
@@ -38,6 +46,13 @@ import org.junit.runners.model.Statement;
* finished outer rule
* </pre>
*
+ * In older versions of JUnit (before 4.13) {@code RuleChain} was used for
+ * ordering rules. We recommend to not use it for this purpose anymore. You can
+ * use the attribute {@code order} of the annotation {@link Rule#order() Rule}
+ * or {@link org.junit.ClassRule#order() ClassRule} for ordering rules.
+ *
+ * @see org.junit.Rule#order()
+ * @see org.junit.ClassRule#order()
* @since 4.10
*/
public class RuleChain implements TestRule {
@@ -72,13 +87,17 @@ public class RuleChain implements TestRule {
}
/**
- * Create a new {@code RuleChain}, which encloses the {@code nextRule} with
+ * Create a new {@code RuleChain}, which encloses the given {@link TestRule} with
* the rules of the current {@code RuleChain}.
*
- * @param enclosedRule the rule to enclose.
+ * @param enclosedRule the rule to enclose; must not be {@code null}.
* @return a new {@code RuleChain}.
+ * @throws NullPointerException if the argument {@code enclosedRule} is {@code null}
*/
public RuleChain around(TestRule enclosedRule) {
+ if (enclosedRule == null) {
+ throw new NullPointerException("The enclosed rule must not be null");
+ }
List<TestRule> rulesOfNewChain = new ArrayList<TestRule>();
rulesOfNewChain.add(enclosedRule);
rulesOfNewChain.addAll(rulesStartingWithInnerMost);
@@ -89,9 +108,6 @@ public class RuleChain implements TestRule {
* {@inheritDoc}
*/
public Statement apply(Statement base, Description description) {
- for (TestRule each : rulesStartingWithInnerMost) {
- base = each.apply(base, description);
- }
- return base;
+ return new RunRules(base, rulesStartingWithInnerMost, description);
}
} \ No newline at end of file
diff --git a/src/main/java/org/junit/rules/Stopwatch.java b/src/main/java/org/junit/rules/Stopwatch.java
index 5d34e7f..6900a48 100644
--- a/src/main/java/org/junit/rules/Stopwatch.java
+++ b/src/main/java/org/junit/rules/Stopwatch.java
@@ -76,7 +76,7 @@ import java.util.concurrent.TimeUnit;
* @author tibor17
* @since 4.12
*/
-public abstract class Stopwatch implements TestRule {
+public class Stopwatch implements TestRule {
private final Clock clock;
private volatile long startNanos;
private volatile long endNanos;
diff --git a/src/main/java/org/junit/rules/TemporaryFolder.java b/src/main/java/org/junit/rules/TemporaryFolder.java
index dc75c93..a726c66 100644
--- a/src/main/java/org/junit/rules/TemporaryFolder.java
+++ b/src/main/java/org/junit/rules/TemporaryFolder.java
@@ -1,15 +1,20 @@
package org.junit.rules;
+import static org.junit.Assert.fail;
+
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import org.junit.Rule;
/**
* The TemporaryFolder Rule allows creation of files and folders that should
* be deleted when the test method finishes (whether it passes or
- * fails). Whether the deletion is successful or not is not checked by this rule.
- * No exception will be thrown in case the deletion fails.
+ * fails).
+ * By default no exception will be thrown in case the deletion fails.
*
* <p>Example of usage:
* <pre>
@@ -26,18 +31,104 @@ import org.junit.Rule;
* }
* </pre>
*
+ * <p>TemporaryFolder rule supports assured deletion mode, which
+ * will fail the test in case deletion fails with {@link AssertionError}.
+ *
+ * <p>Creating TemporaryFolder with assured deletion:
+ * <pre>
+ * &#064;Rule
+ * public TemporaryFolder folder= TemporaryFolder.builder().assureDeletion().build();
+ * </pre>
+ *
* @since 4.7
*/
public class TemporaryFolder extends ExternalResource {
private final File parentFolder;
+ private final boolean assureDeletion;
private File folder;
+ private static final int TEMP_DIR_ATTEMPTS = 10000;
+ private static final String TMP_PREFIX = "junit";
+
+ /**
+ * Create a temporary folder which uses system default temporary-file
+ * directory to create temporary resources.
+ */
public TemporaryFolder() {
- this(null);
+ this((File) null);
}
+ /**
+ * Create a temporary folder which uses the specified directory to create
+ * temporary resources.
+ *
+ * @param parentFolder folder where temporary resources will be created.
+ * If {@code null} then system default temporary-file directory is used.
+ */
public TemporaryFolder(File parentFolder) {
this.parentFolder = parentFolder;
+ this.assureDeletion = false;
+ }
+
+ /**
+ * Create a {@link TemporaryFolder} initialized with
+ * values from a builder.
+ */
+ protected TemporaryFolder(Builder builder) {
+ this.parentFolder = builder.parentFolder;
+ this.assureDeletion = builder.assureDeletion;
+ }
+
+ /**
+ * Returns a new builder for building an instance of {@link TemporaryFolder}.
+ *
+ * @since 4.13
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builds an instance of {@link TemporaryFolder}.
+ *
+ * @since 4.13
+ */
+ public static class Builder {
+ private File parentFolder;
+ private boolean assureDeletion;
+
+ protected Builder() {}
+
+ /**
+ * Specifies which folder to use for creating temporary resources.
+ * If {@code null} then system default temporary-file directory is
+ * used.
+ *
+ * @return this
+ */
+ public Builder parentFolder(File parentFolder) {
+ this.parentFolder = parentFolder;
+ return this;
+ }
+
+ /**
+ * Setting this flag assures that no resources are left undeleted. Failure
+ * to fulfill the assurance results in failure of tests with an
+ * {@link AssertionError}.
+ *
+ * @return this
+ */
+ public Builder assureDeletion() {
+ this.assureDeletion = true;
+ return this;
+ }
+
+ /**
+ * Builds a {@link TemporaryFolder} instance using the values in this builder.
+ */
+ public TemporaryFolder build() {
+ return new TemporaryFolder(this);
+ }
}
@Override
@@ -75,52 +166,63 @@ public class TemporaryFolder extends ExternalResource {
* Returns a new fresh file with a random name under the temporary folder.
*/
public File newFile() throws IOException {
- return File.createTempFile("junit", null, getRoot());
+ return File.createTempFile(TMP_PREFIX, null, getRoot());
}
/**
- * Returns a new fresh folder with the given name under the temporary
+ * Returns a new fresh folder with the given path under the temporary
* folder.
*/
- public File newFolder(String folder) throws IOException {
- return newFolder(new String[]{folder});
+ public File newFolder(String path) throws IOException {
+ return newFolder(new String[]{path});
}
/**
- * Returns a new fresh folder with the given name(s) under the temporary
- * folder.
+ * Returns a new fresh folder with the given paths under the temporary
+ * folder. For example, if you pass in the strings {@code "parent"} and {@code "child"}
+ * then a directory named {@code "parent"} will be created under the temporary folder
+ * and a directory named {@code "child"} will be created under the newly-created
+ * {@code "parent"} directory.
*/
- public File newFolder(String... folderNames) throws IOException {
- File file = getRoot();
- for (int i = 0; i < folderNames.length; i++) {
- String folderName = folderNames[i];
- validateFolderName(folderName);
- file = new File(file, folderName);
- if (!file.mkdir() && isLastElementInArray(i, folderNames)) {
- throw new IOException(
- "a folder with the name \'" + folderName + "\' already exists");
- }
+ public File newFolder(String... paths) throws IOException {
+ if (paths.length == 0) {
+ throw new IllegalArgumentException("must pass at least one path");
}
- return file;
- }
-
- /**
- * Validates if multiple path components were used while creating a folder.
- *
- * @param folderName
- * Name of the folder being created
- */
- private void validateFolderName(String folderName) throws IOException {
- File tempFile = new File(folderName);
- if (tempFile.getParent() != null) {
- String errorMsg = "Folder name cannot consist of multiple path components separated by a file separator."
- + " Please use newFolder('MyParentFolder','MyFolder') to create hierarchies of folders";
- throw new IOException(errorMsg);
+
+ /*
+ * Before checking if the paths are absolute paths, check if create() was ever called,
+ * and if it wasn't, throw IllegalStateException.
+ */
+ File root = getRoot();
+ for (String path : paths) {
+ if (new File(path).isAbsolute()) {
+ throw new IOException("folder path \'" + path + "\' is not a relative path");
+ }
}
- }
- private boolean isLastElementInArray(int index, String[] array) {
- return index == array.length - 1;
+ File relativePath = null;
+ File file = root;
+ boolean lastMkdirsCallSuccessful = true;
+ for (String path : paths) {
+ relativePath = new File(relativePath, path);
+ file = new File(root, relativePath.getPath());
+
+ lastMkdirsCallSuccessful = file.mkdirs();
+ if (!lastMkdirsCallSuccessful && !file.isDirectory()) {
+ if (file.exists()) {
+ throw new IOException(
+ "a file with the path \'" + relativePath.getPath() + "\' exists");
+ } else {
+ throw new IOException(
+ "could not create a folder with the path \'" + relativePath.getPath() + "\'");
+ }
+ }
+ }
+ if (!lastMkdirsCallSuccessful) {
+ throw new IOException(
+ "a folder with the path \'" + relativePath.getPath() + "\' already exists");
+ }
+ return file;
}
/**
@@ -130,11 +232,63 @@ public class TemporaryFolder extends ExternalResource {
return createTemporaryFolderIn(getRoot());
}
- private File createTemporaryFolderIn(File parentFolder) throws IOException {
- File createdFolder = File.createTempFile("junit", "", parentFolder);
- createdFolder.delete();
- createdFolder.mkdir();
- return createdFolder;
+ private static File createTemporaryFolderIn(File parentFolder) throws IOException {
+ try {
+ return createTemporaryFolderWithNioApi(parentFolder);
+ } catch (ClassNotFoundException ignore) {
+ // Fallback for Java 5 and 6
+ return createTemporaryFolderWithFileApi(parentFolder);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ }
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ }
+ IOException exception = new IOException("Failed to create temporary folder in " + parentFolder);
+ exception.initCause(cause);
+ throw exception;
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create temporary folder in " + parentFolder, e);
+ }
+ }
+
+ private static File createTemporaryFolderWithNioApi(File parentFolder) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ Class<?> filesClass = Class.forName("java.nio.file.Files");
+ Object fileAttributeArray = Array.newInstance(Class.forName("java.nio.file.attribute.FileAttribute"), 0);
+ Class<?> pathClass = Class.forName("java.nio.file.Path");
+ Object tempDir;
+ if (parentFolder != null) {
+ Method createTempDirectoryMethod = filesClass.getDeclaredMethod("createTempDirectory", pathClass, String.class, fileAttributeArray.getClass());
+ Object parentPath = File.class.getDeclaredMethod("toPath").invoke(parentFolder);
+ tempDir = createTempDirectoryMethod.invoke(null, parentPath, TMP_PREFIX, fileAttributeArray);
+ } else {
+ Method createTempDirectoryMethod = filesClass.getDeclaredMethod("createTempDirectory", String.class, fileAttributeArray.getClass());
+ tempDir = createTempDirectoryMethod.invoke(null, TMP_PREFIX, fileAttributeArray);
+ }
+ return (File) pathClass.getDeclaredMethod("toFile").invoke(tempDir);
+ }
+
+ private static File createTemporaryFolderWithFileApi(File parentFolder) throws IOException {
+ File createdFolder = null;
+ for (int i = 0; i < TEMP_DIR_ATTEMPTS; ++i) {
+ // Use createTempFile to get a suitable folder name.
+ String suffix = ".tmp";
+ File tmpFile = File.createTempFile(TMP_PREFIX, suffix, parentFolder);
+ String tmpName = tmpFile.toString();
+ // Discard .tmp suffix of tmpName.
+ String folderName = tmpName.substring(0, tmpName.length() - suffix.length());
+ createdFolder = new File(folderName);
+ if (createdFolder.mkdir()) {
+ tmpFile.delete();
+ return createdFolder;
+ }
+ tmpFile.delete();
+ }
+ throw new IOException("Unable to create temporary directory in: "
+ + parentFolder.toString() + ". Tried " + TEMP_DIR_ATTEMPTS + " times. "
+ + "Last attempted to create: " + createdFolder.toString());
}
/**
@@ -150,21 +304,48 @@ public class TemporaryFolder extends ExternalResource {
/**
* Delete all files and folders under the temporary folder. Usually not
- * called directly, since it is automatically applied by the {@link Rule}
+ * called directly, since it is automatically applied by the {@link Rule}.
+ *
+ * @throws AssertionError if unable to clean up resources
+ * and deletion of resources is assured.
*/
public void delete() {
- if (folder != null) {
- recursiveDelete(folder);
+ if (!tryDelete()) {
+ if (assureDeletion) {
+ fail("Unable to clean up temporary folder " + folder);
+ }
+ }
+ }
+
+ /**
+ * Tries to delete all files and folders under the temporary folder and
+ * returns whether deletion was successful or not.
+ *
+ * @return {@code true} if all resources are deleted successfully,
+ * {@code false} otherwise.
+ */
+ private boolean tryDelete() {
+ if (folder == null) {
+ return true;
}
+
+ return recursiveDelete(folder);
}
- private void recursiveDelete(File file) {
+ private boolean recursiveDelete(File file) {
+ // Try deleting file before assuming file is a directory
+ // to prevent following symbolic links.
+ if (file.delete()) {
+ return true;
+ }
File[] files = file.listFiles();
if (files != null) {
for (File each : files) {
- recursiveDelete(each);
+ if (!recursiveDelete(each)) {
+ return false;
+ }
}
}
- file.delete();
+ return file.delete();
}
}
diff --git a/src/main/java/org/junit/rules/TestName.java b/src/main/java/org/junit/rules/TestName.java
index bf72602..e2ebc2e 100644
--- a/src/main/java/org/junit/rules/TestName.java
+++ b/src/main/java/org/junit/rules/TestName.java
@@ -25,7 +25,7 @@ import org.junit.runner.Description;
* @since 4.7
*/
public class TestName extends TestWatcher {
- private String name;
+ private volatile String name;
@Override
protected void starting(Description d) {
diff --git a/src/main/java/org/junit/rules/TestWatcher.java b/src/main/java/org/junit/rules/TestWatcher.java
index 5492b6b..a28514d 100644
--- a/src/main/java/org/junit/rules/TestWatcher.java
+++ b/src/main/java/org/junit/rules/TestWatcher.java
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import org.junit.AssumptionViolatedException;
+import org.junit.Rule;
import org.junit.runner.Description;
import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
@@ -17,7 +18,7 @@ import org.junit.runners.model.Statement;
* public static class WatchmanTest {
* private static String watchedLog;
*
- * &#064;Rule
+ * &#064;Rule(order = Integer.MIN_VALUE)
* public TestWatcher watchman= new TestWatcher() {
* &#064;Override
* protected void failed(Throwable e, Description description) {
@@ -40,6 +41,11 @@ import org.junit.runners.model.Statement;
* }
* }
* </pre>
+ * <p>It is recommended to always set the {@link Rule#order() order} of the
+ * {@code TestWatcher} to {@code Integer.MIN_VALUE} so that it encloses all
+ * other rules. Otherwise it may see failed tests as successful and vice versa
+ * if some rule changes the result of a test (e.g. {@link ErrorCollector} or
+ * {@link ExpectedException}).
*
* @since 4.9
*/
@@ -54,7 +60,7 @@ public abstract class TestWatcher implements TestRule {
try {
base.evaluate();
succeededQuietly(description, errors);
- } catch (@SuppressWarnings("deprecation") org.junit.internal.AssumptionViolatedException e) {
+ } catch (org.junit.internal.AssumptionViolatedException e) {
errors.add(e);
skippedQuietly(e, description, errors);
} catch (Throwable e) {
@@ -87,7 +93,6 @@ public abstract class TestWatcher implements TestRule {
}
}
- @SuppressWarnings("deprecation")
private void skippedQuietly(
org.junit.internal.AssumptionViolatedException e, Description description,
List<Throwable> errors) {
@@ -135,7 +140,6 @@ public abstract class TestWatcher implements TestRule {
/**
* Invoked when a test is skipped due to a failed assumption.
*/
- @SuppressWarnings("deprecation")
protected void skipped(AssumptionViolatedException e, Description description) {
// For backwards compatibility with JUnit 4.11 and earlier, call the legacy version
org.junit.internal.AssumptionViolatedException asInternalException = e;
diff --git a/src/main/java/org/junit/rules/Timeout.java b/src/main/java/org/junit/rules/Timeout.java
index 8d382df..5cf905a 100644
--- a/src/main/java/org/junit/rules/Timeout.java
+++ b/src/main/java/org/junit/rules/Timeout.java
@@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit;
* public static class HasGlobalLongTimeout {
*
* &#064;Rule
- * public Timeout globalTimeout= new Timeout(20);
+ * public Timeout globalTimeout = Timeout.millis(20);
*
* &#064;Test
* public void run1() throws InterruptedException {
@@ -82,7 +82,7 @@ public class Timeout implements TestRule {
}
/**
- * Create a {@code Timeout} instance initialized with values form
+ * Create a {@code Timeout} instance initialized with values from
* a builder.
*
* @since 4.12