blob: 839fbacaa961ab2180de2db706bd8ab4fcdb49f2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.drill.test;
import org.apache.commons.io.FileUtils;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* <p>
* This class is used to create consistently named and safe temp directories for
* unit tests.
* </p>
* <p>
* A {@link DirTestWatcher} is added to a test by declaring it as a JUnit
* {@link org.junit.Rule}. A {@link org.junit.Rule Rule} is a piece of code that
* is run before and after every JUnit test marked with the
* {@link org.junit.Test Test} annotation. When the {@link DirTestWatcher} is
* added to a test class the {@link DirTestWatcher} will create a temp directory
* before each of your {@link org.junit.Test Test}s and optionally delete the
* temp directory after each of your {@link org.junit.Test Test}s. The <b>base
* temp directory</b> created by the {@link DirTestWatcher} is in the
* <b>target</b> folder of the maven project and has the form <b>(my test class
* fully qualified name)/(my test method name)</b>. So in the context of the
* code example below, the temp directory created for each test in <b>target</b>
* will be <b>my.proj.MyTestClass/myTestMethod1</b> and
* <b>my.proj.MyTestClass/myTestMethod2</b> respectively.
* </p>
* <p>
* The temp directory created by the {@link DirTestWatcher} can be used within a
* test by simply calling the {@link DirTestWatcher#getDir()} method on the
* {@link DirTestWatcher} within your unit test.
* </p>
*
* <p>
* By default, the {@link DirTestWatcher} deletes the temp directory it creates
* at the end of each {@link org.junit.Test Test}. However, you can create a
* {@link DirTestWatcher} by doing {@code new DirTestWatcher(false)} to disable
* the deletion of temp directories after a test. This is useful if you want to
* examine files after a test runs.
* </p>
*
* <pre>
* <code>
* package my.proj;
*
* public class MyTestClass {
* &#064;Rule
* public final DirTestWatcher dirTestWatcher = new DirTestWatcher();
*
* &#064;Test
* public void myTestMethod1() {
* File dir = dirTestWatcher.getDir();
* // Do stuff in the temp directory
* }
*
* &#064;Test
* public void myTestMethod2() {
* File dir = dirTestWatcher.getDir();
* // Do stuff in the temp directory
* }
* }
* </code>
* </pre>
*
* <p>
* <b>Note:</b> In the code sample above, the directories returned by
* {@link DirTestWatcher#getDir()} in myTestMethod1 and myTestMethod2 are
* <b>my.proj.MyTestClass/myTestMethod1</b> and
* <b>my.proj.MyTestClass/myTestMethod2</b> respectively.
* </p>
*/
public class DirTestWatcher extends TestWatcher {
private static final int TEMP_DIR_ATTEMPTS = 10000;
private String dirPath;
private File dir;
private boolean deleteDirAtEnd = true;
/**
* Creates a {@link DirTestWatcher} that deletes temp directories after the
* {@link TestWatcher} completes.
*/
public DirTestWatcher() {
}
/**
* Creates a {@link DirTestWatcher} which can delete or keep the temp
* directory after the {@link TestWatcher} completes.
*
* @param deleteDirAtEnd
* When true the temp directory created by the {@link DirTestWatcher}
* is deleted. When false the temp directory created by the
* {@link DirTestWatcher} is not deleted.
*/
public DirTestWatcher(boolean deleteDirAtEnd) {
this.deleteDirAtEnd = deleteDirAtEnd;
}
@Override
protected void starting(Description description) {
if (description.getMethodName() != null) {
dirPath = Paths.get(".", "target", description.getClassName(),
description.getMethodName()).toString();
} else {
dirPath = Paths.get(".", "target", description.getClassName()).toString();
}
dir = new File(dirPath);
FileUtils.deleteQuietly(dir);
dir.mkdirs();
}
@Override
protected void finished(Description description) {
deleteDir();
}
@Override
protected void failed(Throwable e, Description description) {
deleteDir();
}
/**
* Creates a sub directory with the given relative path in current temp
* directory
*
* @param relativeDirPath
* The relative path of the sub directory to create in the current
* temp directory.
* @return The {@link java.io.File} object of the newly created sub directory.
*/
public File makeSubDir(Path relativeDirPath) {
File subDir = dir.toPath().resolve(relativeDirPath).toFile();
subDir.mkdirs();
return subDir;
}
private void deleteDir() {
if (deleteDirAtEnd) {
FileUtils.deleteQuietly(dir);
}
}
/**
* Gets the {@link java.io.File} object of the current temp directory.
*
* @return The {@link java.io.File} object of the current temp directory.
*/
public File getDir() {
return dir;
}
public static File createTempDir(File baseDir) {
String baseName = System.currentTimeMillis() + "-";
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdirs()) {
return tempDir;
}
}
String message = String.format(
"Failed to create directory within %s attempts (tried %s0 to %s)",
TEMP_DIR_ATTEMPTS, baseName, baseName + (TEMP_DIR_ATTEMPTS - 1));
throw new IllegalStateException(message);
}
}