001package org.junit.rules;
002
003import java.io.File;
004import java.io.IOException;
005
006import org.junit.Rule;
007
008/**
009 * The TemporaryFolder Rule allows creation of files and folders that are
010 * guaranteed to be deleted when the test method finishes (whether it passes or
011 * fails):
012 *
013 * <pre>
014 * public static class HasTempFolder {
015 *  &#064;Rule
016 *  public TemporaryFolder folder= new TemporaryFolder();
017 *
018 *  &#064;Test
019 *  public void testUsingTempFolder() throws IOException {
020 *      File createdFile= folder.newFile(&quot;myfile.txt&quot;);
021 *      File createdFolder= folder.newFolder(&quot;subfolder&quot;);
022 *      // ...
023 *     }
024 * }
025 * </pre>
026 *
027 * @since 4.7
028 */
029public class TemporaryFolder extends ExternalResource {
030    private final File parentFolder;
031    private File folder;
032
033    public TemporaryFolder() {
034        this(null);
035    }
036
037    public TemporaryFolder(File parentFolder) {
038        this.parentFolder = parentFolder;
039    }
040
041    @Override
042    protected void before() throws Throwable {
043        create();
044    }
045
046    @Override
047    protected void after() {
048        delete();
049    }
050
051    // testing purposes only
052
053    /**
054     * for testing purposes only. Do not use.
055     */
056    public void create() throws IOException {
057        folder = createTemporaryFolderIn(parentFolder);
058    }
059
060    /**
061     * Returns a new fresh file with the given name under the temporary folder.
062     */
063    public File newFile(String fileName) throws IOException {
064        File file = new File(getRoot(), fileName);
065        if (!file.createNewFile()) {
066            throw new IOException(
067                    "a file with the name \'" + fileName + "\' already exists in the test folder");
068        }
069        return file;
070    }
071
072    /**
073     * Returns a new fresh file with a random name under the temporary folder.
074     */
075    public File newFile() throws IOException {
076        return File.createTempFile("junit", null, getRoot());
077    }
078
079    /**
080     * Returns a new fresh folder with the given name under the temporary
081     * folder.
082     */
083    public File newFolder(String folder) throws IOException {
084        return newFolder(new String[]{folder});
085    }
086
087    /**
088     * Returns a new fresh folder with the given name(s) under the temporary
089     * folder.
090     */
091    public File newFolder(String... folderNames) throws IOException {
092        File file = getRoot();
093        for (int i = 0; i < folderNames.length; i++) {
094            String folderName = folderNames[i];
095            file = new File(file, folderName);
096            if (!file.mkdir() && isLastElementInArray(i, folderNames)) {
097                throw new IOException(
098                        "a folder with the name \'" + folderName + "\' already exists");
099            }
100        }
101        return file;
102    }
103
104    private boolean isLastElementInArray(int index, String[] array) {
105        return index == array.length - 1;
106    }
107
108    /**
109     * Returns a new fresh folder with a random name under the temporary folder.
110     */
111    public File newFolder() throws IOException {
112        return createTemporaryFolderIn(getRoot());
113    }
114
115    private File createTemporaryFolderIn(File parentFolder) throws IOException {
116        File createdFolder = File.createTempFile("junit", "", parentFolder);
117        createdFolder.delete();
118        createdFolder.mkdir();
119        return createdFolder;
120    }
121
122    /**
123     * @return the location of this temporary folder.
124     */
125    public File getRoot() {
126        if (folder == null) {
127            throw new IllegalStateException(
128                    "the temporary folder has not yet been created");
129        }
130        return folder;
131    }
132
133    /**
134     * Delete all files and folders under the temporary folder. Usually not
135     * called directly, since it is automatically applied by the {@link Rule}
136     */
137    public void delete() {
138        if (folder != null) {
139            recursiveDelete(folder);
140        }
141    }
142
143    private void recursiveDelete(File file) {
144        File[] files = file.listFiles();
145        if (files != null) {
146            for (File each : files) {
147                recursiveDelete(each);
148            }
149        }
150        file.delete();
151    }
152}