001package org.junit.rules; 002 003import java.io.File; 004import java.io.IOException; 005import java.lang.reflect.Array; 006import java.lang.reflect.InvocationTargetException; 007import java.lang.reflect.Method; 008 009import org.junit.Rule; 010 011/** 012 * The TemporaryFolder Rule allows creation of files and folders that should 013 * be deleted when the test method finishes (whether it passes or 014 * fails). Whether the deletion is successful or not is not checked by this rule. 015 * No exception will be thrown in case the deletion fails. 016 * 017 * <p>Example of usage: 018 * <pre> 019 * public static class HasTempFolder { 020 * @Rule 021 * public TemporaryFolder folder= new TemporaryFolder(); 022 * 023 * @Test 024 * public void testUsingTempFolder() throws IOException { 025 * File createdFile= folder.newFile("myfile.txt"); 026 * File createdFolder= folder.newFolder("subfolder"); 027 * // ... 028 * } 029 * } 030 * </pre> 031 * 032 * @since 4.7 033 */ 034public class TemporaryFolder extends ExternalResource { 035 private final File parentFolder; 036 private File folder; 037 038 public TemporaryFolder() { 039 this(null); 040 } 041 042 public TemporaryFolder(File parentFolder) { 043 this.parentFolder = parentFolder; 044 } 045 046 @Override 047 protected void before() throws Throwable { 048 create(); 049 } 050 051 @Override 052 protected void after() { 053 delete(); 054 } 055 056 // testing purposes only 057 058 /** 059 * for testing purposes only. Do not use. 060 */ 061 public void create() throws IOException { 062 folder = createTemporaryFolderIn(parentFolder); 063 } 064 065 /** 066 * Returns a new fresh file with the given name under the temporary folder. 067 */ 068 public File newFile(String fileName) throws IOException { 069 File file = new File(getRoot(), fileName); 070 if (!file.createNewFile()) { 071 throw new IOException( 072 "a file with the name \'" + fileName + "\' already exists in the test folder"); 073 } 074 return file; 075 } 076 077 /** 078 * Returns a new fresh file with a random name under the temporary folder. 079 */ 080 public File newFile() throws IOException { 081 return File.createTempFile("junit", null, getRoot()); 082 } 083 084 /** 085 * Returns a new fresh folder with the given name under the temporary 086 * folder. 087 */ 088 public File newFolder(String folder) throws IOException { 089 return newFolder(new String[]{folder}); 090 } 091 092 /** 093 * Returns a new fresh folder with the given name(s) under the temporary 094 * folder. 095 */ 096 public File newFolder(String... folderNames) throws IOException { 097 File file = getRoot(); 098 for (int i = 0; i < folderNames.length; i++) { 099 String folderName = folderNames[i]; 100 validateFolderName(folderName); 101 file = new File(file, folderName); 102 if (!file.mkdir() && isLastElementInArray(i, folderNames)) { 103 throw new IOException( 104 "a folder with the name \'" + folderName + "\' already exists"); 105 } 106 } 107 return file; 108 } 109 110 /** 111 * Validates if multiple path components were used while creating a folder. 112 * 113 * @param folderName 114 * Name of the folder being created 115 */ 116 private void validateFolderName(String folderName) throws IOException { 117 File tempFile = new File(folderName); 118 if (tempFile.getParent() != null) { 119 String errorMsg = "Folder name cannot consist of multiple path components separated by a file separator." 120 + " Please use newFolder('MyParentFolder','MyFolder') to create hierarchies of folders"; 121 throw new IOException(errorMsg); 122 } 123 } 124 125 private boolean isLastElementInArray(int index, String[] array) { 126 return index == array.length - 1; 127 } 128 129 /** 130 * Returns a new fresh folder with a random name under the temporary folder. 131 */ 132 public File newFolder() throws IOException { 133 return createTemporaryFolderIn(getRoot()); 134 } 135 136 private static File createTemporaryFolderIn(File parentFolder) throws IOException { 137 try { 138 return createTemporaryFolderWithNioApi(parentFolder); 139 } catch (ClassNotFoundException ignore) { 140 // Fallback for Java 5 and 6 141 return createTemporaryFolderWithFileApi(parentFolder); 142 } catch (InvocationTargetException e) { 143 Throwable cause = e.getCause(); 144 if (cause instanceof IOException) { 145 throw (IOException) cause; 146 } 147 if (cause instanceof RuntimeException) { 148 throw (RuntimeException) cause; 149 } 150 IOException exception = new IOException("Failed to create temporary folder in " + parentFolder); 151 exception.initCause(cause); 152 throw exception; 153 } catch (Exception e) { 154 throw new RuntimeException("Failed to create temporary folder in " + parentFolder, e); 155 } 156 } 157 158 private static File createTemporaryFolderWithNioApi(File parentFolder) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { 159 Class<?> filesClass = Class.forName("java.nio.file.Files"); 160 Object fileAttributeArray = Array.newInstance(Class.forName("java.nio.file.attribute.FileAttribute"), 0); 161 Class<?> pathClass = Class.forName("java.nio.file.Path"); 162 Object tempDir; 163 if (parentFolder != null) { 164 Method createTempDirectoryMethod = filesClass.getDeclaredMethod("createTempDirectory", pathClass, String.class, fileAttributeArray.getClass()); 165 Object parentPath = File.class.getDeclaredMethod("toPath").invoke(parentFolder); 166 tempDir = createTempDirectoryMethod.invoke(null, parentPath, "junit", fileAttributeArray); 167 } else { 168 Method createTempDirectoryMethod = filesClass.getDeclaredMethod("createTempDirectory", String.class, fileAttributeArray.getClass()); 169 tempDir = createTempDirectoryMethod.invoke(null, "junit", fileAttributeArray); 170 } 171 return (File) pathClass.getDeclaredMethod("toFile").invoke(tempDir); 172 } 173 174 private static File createTemporaryFolderWithFileApi(File parentFolder) throws IOException { 175 File createdFolder = File.createTempFile("junit", "", parentFolder); 176 createdFolder.delete(); 177 createdFolder.mkdir(); 178 return createdFolder; 179 } 180 181 /** 182 * @return the location of this temporary folder. 183 */ 184 public File getRoot() { 185 if (folder == null) { 186 throw new IllegalStateException( 187 "the temporary folder has not yet been created"); 188 } 189 return folder; 190 } 191 192 /** 193 * Delete all files and folders under the temporary folder. Usually not 194 * called directly, since it is automatically applied by the {@link Rule} 195 */ 196 public void delete() { 197 if (folder != null) { 198 recursiveDelete(folder); 199 } 200 } 201 202 private void recursiveDelete(File file) { 203 File[] files = file.listFiles(); 204 if (files != null) { 205 for (File each : files) { 206 recursiveDelete(each); 207 } 208 } 209 file.delete(); 210 } 211}