diff options
author | Pete Bentley <prb@google.com> | 2021-03-03 15:09:19 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-03-03 15:09:19 +0000 |
commit | 4ea71685c0a1bda87567b2d1ef5ed651d9c2608b (patch) | |
tree | 10923530939981dd1dc5f44ceb6316fb9442428a /src/main/java/org/junit/rules | |
parent | 5cb7d97d73027edb2274ac39e8a958bab5e2e7b3 (diff) | |
parent | a9a7715d84046efd231e038b0dc2f551daf61701 (diff) | |
download | junit-4ea71685c0a1bda87567b2d1ef5ed651d9c2608b.tar.gz |
Merge changes I578a2676,I4b37c2d0,Id1e2d638,I1ebe37da,I6135799c am: b6446bec0a am: fcd81b3e3f am: c7a6d4ec7d am: a9a7715d84
Original change: https://android-review.googlesource.com/c/platform/external/junit/+/1613132
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I56f45d05317bd1060b5838f1ada3a90bb2985d55
Diffstat (limited to 'src/main/java/org/junit/rules')
-rw-r--r-- | src/main/java/org/junit/rules/ErrorCollector.java | 42 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/ExpectedException.java | 57 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/ExternalResource.java | 15 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/MethodRule.java | 16 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/RuleChain.java | 50 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/Stopwatch.java | 2 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/TemporaryFolder.java | 277 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/TestName.java | 2 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/TestWatcher.java | 12 | ||||
-rw-r--r-- | src/main/java/org/junit/rules/Timeout.java | 4 |
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 { * @Rule - * public ExpectedException thrown= ExpectedException.none(); + * public ExpectedException thrown = ExpectedException.none(); * * @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> @Test * public void throwsException() { @@ -63,9 +64,15 @@ import org.junit.runners.model.Statement; * throw new NullPointerException("What happened?"); * }</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> @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("What happened?", 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 { - * @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 { + * @Rule + * public final TestRule extendedLogging = CompositeRules.extendedLogging(); * - * @Test - * public void example() { - * assertTrue(true); - * } + * @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> + * @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; * - * @Rule + * @Rule(order = Integer.MIN_VALUE) * public TestWatcher watchman= new TestWatcher() { * @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 { * * @Rule - * public Timeout globalTimeout= new Timeout(20); + * public Timeout globalTimeout = Timeout.millis(20); * * @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 |