/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.utils.incubator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import de.rcenvironment.core.utils.common.OSFamily;
import de.rcenvironment.core.utils.common.TempFileService;
import de.rcenvironment.core.utils.common.TempFileServiceAccess;
/**
* Unit test for {@link FileSystemOperations} methods.
*
* @author Robert Mischke
*/
public class FileSystemOperationsTest {
private static final String TEST_SUBDIR_NAME = "subdir";
private static final String TEST_FILENAME_2 = "file2.txt";
private static final String TEST_FILENAME_1 = "file1.txt";
private TempFileService tempFileService;
private final Log log = LogFactory.getLog(getClass());
private File sandboxDir;
private File testRootDir;
/**
* Common test setup.
*
* @throws IOException on setup errors
*/
@Before
public void setup() throws IOException {
TempFileServiceAccess.setupUnitTestEnvironment();
tempFileService = TempFileServiceAccess.getInstance();
testRootDir = tempFileService.createManagedTempDir();
sandboxDir = createAndVerifySubdir(testRootDir, "sandbox");
log.debug("Testing in temporary directory " + testRootDir.getAbsolutePath());
}
/**
* Test that a simple sandbox with a few files is properly deleted.
*
* @throws Exception on uncaught exceptions
*/
@Test
public void testBasicSandboxDeletion() throws Exception {
createAndVerifyFile(sandboxDir, TEST_FILENAME_1);
File subDir = createAndVerifySubdir(sandboxDir, TEST_SUBDIR_NAME);
createAndVerifyFile(subDir, TEST_FILENAME_2);
assertEquals(2, sandboxDir.listFiles().length);
FileSystemOperations.deleteSandboxDirectory(sandboxDir);
assertFalse(sandboxDir.exists());
}
/**
* Tests that a symbolic link in the sandbox directory that points to another file in the sandbox directory is properly deleted.
*
* @throws Exception on uncaught exceptions
*/
@Test
public void testInternalLinkDeletion() throws Exception {
// Manipulating symlinks requires special permissions on Windows, so this cannot be tested out-of-the-box;
// Current solution: check if symlinks can be created, else use junctions (for directories) and hard links (for files) instead.
// create internal file link
File targetFile = createAndVerifyFile(sandboxDir, TEST_FILENAME_1);
File symLinkToTargetFile = new File(sandboxDir, "symLinkToFile");
createOSSpecificTestLink(symLinkToTargetFile, targetFile);
// create internal directory link
File subDir = createAndVerifySubdir(sandboxDir, TEST_SUBDIR_NAME);
createAndVerifyFile(subDir, TEST_FILENAME_2);
File symLinkToSubDir = new File(sandboxDir, "symlinkToSubDir");
createOSSpecificTestLink(symLinkToSubDir, subDir);
assertEquals(4, sandboxDir.listFiles().length); // file+link, directory+link
assertEquals(1, subDir.listFiles().length);
assertEquals(1, symLinkToSubDir.listFiles().length); // should "look" like the actual external directory
FileSystemOperations.deleteSandboxDirectory(sandboxDir);
assertFalse(sandboxDir.exists());
}
/**
* Tests that a symbolic link in the sandbox directory that points to a file outside of the sandbox directory does *not* cause the
* link's target to be deleted.
*
* @throws Exception on uncaught exceptions
*/
@Test
// @Ignore("WIP; not complete yet")
public void testExternalLinkTargetSurvival() throws Exception {
// Manipulating symlinks requires special permissions on Windows, so this cannot be tested out-of-the-box;
// Current solution: check if symlinks can be created, else use junctions (for directories) and hard links (for files) instead.
// create link to external file
File symLinkToFile = new File(sandboxDir, "toExternalFile");
File externalFile = createAndVerifyFile(testRootDir, "externalFile.txt");
createOSSpecificTestLink(symLinkToFile, externalFile);
// create link to external directory
File symLinkToDir = new File(sandboxDir, "toExternalDir");
File subDir = createAndVerifySubdir(sandboxDir, TEST_SUBDIR_NAME);
createAndVerifyFile(subDir, TEST_FILENAME_1);
File externalDir = createAndVerifySubdir(testRootDir, "externalDir");
File fileInExternalDir = createAndVerifyFile(externalDir, "fileInExternalDir");
createOSSpecificTestLink(symLinkToDir, externalDir);
assertEquals(3, sandboxDir.listFiles().length); // normal directory, file symlink, and directory symlink
assertEquals(1, externalDir.listFiles().length);
assertEquals(1, symLinkToDir.listFiles().length); // should "look" like the actual external directory
// verify external files' existence just before deletion
assertTrue(externalDir.isDirectory());
assertTrue(fileInExternalDir.isFile());
assertTrue(externalFile.isFile());
FileSystemOperations.deleteSandboxDirectory(sandboxDir);
assertFalse(sandboxDir.exists());
// check external files' "survival"
assertTrue(externalDir.isDirectory());
assertTrue(fileInExternalDir.isFile());
assertTrue(externalFile.isFile());
}
private File createAndVerifySubdir(File parentDir, String name) {
File dir = new File(parentDir, name);
dir.mkdir();
assertTrue(dir.isDirectory());
return dir;
}
private File createAndVerifyFile(File parentDir, String name) throws IOException {
File file = new File(parentDir, name);
file.createNewFile();
assertTrue(file.isFile() && file.canRead());
return file;
}
private void createOSSpecificTestLink(File linkDir, File linkTargetDir) throws IOException, InterruptedException {
Path linkPath = linkDir.toPath();
Path linkTargetPath = linkTargetDir.toPath();
createOSSpecificTestLink(linkPath, linkTargetPath);
}
private void createOSSpecificTestLink(Path linkPath, Path linkTargetPath) throws IOException, InterruptedException {
if (OSFamily.isLinux()) {
// use Java 7 code to symlink on Linux
Files.createSymbolicLink(linkPath, linkTargetPath, new FileAttribute<?>[0]);
} else if (OSFamily.isWindows()) {
// Windows requires a special permission to create symbolic links, so test with a NTFS Junction (for a directory) or a hardlink
// (for a file) instead if creating symbolic link is not possible.
try {
//WARNING: This could not be tested yet due to missing permissions.
Files.createSymbolicLink(linkPath, linkTargetPath, new FileAttribute<?>[0]);
} catch (FileSystemException e) {
log.debug("Symlink could not be created due to missing permissions. Creating junction/hardlink instead.");
if (Files.isDirectory(linkTargetPath)) {
Process process = Runtime.getRuntime().exec(
new String[] { "cmd.exe", "/c", "mklink", "/j", linkPath.toString(), linkTargetPath.toString() });
assertEquals(0, process.waitFor());
} else {
// Cannot use junctions for simple files, use hard link instead
Process process = Runtime.getRuntime().exec(
new String[] { "cmd.exe", "/c", "mklink", "/h", linkPath.toString(), linkTargetPath.toString() });
assertEquals(0, process.waitFor());
}
}
} else {
throw new IllegalArgumentException(OSFamily.getLocal().toString());
}
}
}