/*
* RHQ Management Platform
* Copyright (C) 2005-2010 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.core.util.file;
import static java.util.Arrays.asList;
import static org.apache.commons.io.FileUtils.deleteDirectory;
import static org.apache.commons.io.FileUtils.toFile;
import static org.apache.commons.io.FileUtils.touch;
import static org.rhq.test.AssertUtils.assertCollectionEqualsNoOrder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.testng.annotations.Test;
import org.rhq.core.util.stream.StreamUtil;
@Test
public class FileUtilTest {
/* I commented out the obfuscate/deobfuscate methods because I didn't have a use for them, but they may be useful
* in the future, so I left the commented code in, so I'm leaving this commented test in. We can resurrect in the future if needed.
*
public void testObfuscateDeobfuscateFile() throws Exception {
System.out.println("testObfuscateDeobfuscateFile");
byte[] line = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ `1234567890-=~!@#$%^&*()_+ []\\{}|;':\",./<>?\n"
.getBytes();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < 10; i++) {
baos.write(line);
}
String data = baos.toString();
File tempFile = File.createTempFile("FileUtilTest", ".txt");
try {
// write the original data in a file
FileUtil.writeFile(new ByteArrayInputStream(data.getBytes()), tempFile);
// compress the file
FileUtil.obfuscateFile(tempFile);
String obfuscatedStr = new String(StreamUtil.slurp(new FileInputStream(tempFile)));
assert !data.equals(obfuscatedStr) : "obfuscated data should be different than original data";
// now deobfuscate it
FileUtil.deobfuscateFile(tempFile);
String deobfuscatedStr = new String(StreamUtil.slurp(new FileInputStream(tempFile)));
assert data.equals(deobfuscatedStr) : "obfuscated data should be same as original data";
assert data.length() == deobfuscatedStr.length() : "data should be equal: " + deobfuscatedStr;
// try to deobfuscate it again - this should fail (its already deobfuscated) but test that the original file is restored
try {
FileUtil.deobfuscateFile(tempFile);
assert false : "Should not have been able to deobfuscate a non-compressed file";
} catch (IOException ioe) {
deobfuscatedStr = new String(StreamUtil.slurp(new FileInputStream(tempFile)));
assert data.equals(deobfuscatedStr) : "data should be same as original data";
assert data.length() == deobfuscatedStr.length() : "data should be equal: " + deobfuscatedStr;
}
} finally {
tempFile.delete();
}
}
*/
public void testCompressDecompressFile() throws Exception {
System.out.println("testCompressDecompressFile");
byte[] line = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ `1234567890-=~!@#$%^&*()_+ []\\{}|;':\",./<>?\n"
.getBytes();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < 10; i++) {
baos.write(line);
}
String data = baos.toString();
File tempFile = File.createTempFile("FileUtilTest", ".txt");
try {
// write the original data in a file
FileUtil.writeFile(new ByteArrayInputStream(data.getBytes()), tempFile);
// compress the file
FileUtil.compressFile(tempFile);
String compressedStr = new String(StreamUtil.slurp(new FileInputStream(tempFile)));
assert !data.equals(compressedStr) : "compressed data should be different than original data";
assert data.length() > compressedStr.length() : "compressed data should be smaller: " + compressedStr;
// now decompress it
FileUtil.decompressFile(tempFile);
String decompressedStr = new String(StreamUtil.slurp(new FileInputStream(tempFile)));
assert data.equals(decompressedStr) : "compressed data should be same as original data";
assert data.length() == decompressedStr.length() : "compressed data should be equal: " + decompressedStr;
// try to decompress it again - this should fail (its already decompressed) but test that the original file is restored
try {
FileUtil.decompressFile(tempFile);
assert false : "Should not have been able to decompress a non-compressed file";
} catch (IOException ioe) {
decompressedStr = new String(StreamUtil.slurp(new FileInputStream(tempFile)));
assert data.equals(decompressedStr) : "data should be same as original data";
assert data.length() == decompressedStr.length() : "data should be equal: " + decompressedStr;
}
} finally {
tempFile.delete();
}
}
public void testIsAbsolutePath() {
assert true == FileUtil.isAbsolutePath("/unix/abs/path");
assert false == FileUtil.isAbsolutePath("unix/rel/path");
if (File.separatorChar == '\\') {
// windows only tests
assert true == FileUtil.isAbsolutePath("C:\\win\\abs\\path");
assert false == FileUtil.isAbsolutePath("C:win\\rel\\path");
assert false == FileUtil.isAbsolutePath("win\\rel\\path");
assert true == FileUtil.isAbsolutePath("\\win\\faux\\abs\\path"); // this is the one we really wanted to test
}
}
public void testIsNewer() throws Exception {
// make sure isNewer doesn't check for null or non-existent Files
File fileDoesNotExist = new File("blah/blah/blah.txt");
assert null == FileUtil.isNewer(null, null);
assert null == FileUtil.isNewer(fileDoesNotExist, null);
assert null == FileUtil.isNewer(null, fileDoesNotExist);
assert null == FileUtil.isNewer(fileDoesNotExist, fileDoesNotExist);
// create a test directory to put some files in
File testDir = FileUtil.createTempDirectory("fileUtilTestIsNewer", ".dest", null);
try {
// make sure isNewer doesn't check directories
assert null == FileUtil.isNewer(testDir, testDir) : "should not have tested directories";
// create some test files to check
File file1 = new File(testDir, "file1.txt");
File file2 = new File(testDir, "file2.txt");
FileUtil.writeFile(new ByteArrayInputStream("test1".getBytes()), file1);
Thread.sleep(2000L); // ensure our last modified time will be different - all platforms support precision in seconds
FileUtil.writeFile(new ByteArrayInputStream("test2".getBytes()), file2);
// make sure isNewer works with normal files
assert Boolean.TRUE.equals(FileUtil.isNewer(file2, file1));
assert Boolean.FALSE.equals(FileUtil.isNewer(file1, file2));
// checking against the same file should return false
assert Boolean.FALSE.equals(FileUtil.isNewer(file1, file1));
assert Boolean.FALSE.equals(FileUtil.isNewer(file2, file2));
// if both files have the same last modified date, then isNewer should return false
long now = System.currentTimeMillis();
file1.setLastModified(now);
file2.setLastModified(now);
assert Boolean.FALSE.equals(FileUtil.isNewer(file1, file2));
assert Boolean.FALSE.equals(FileUtil.isNewer(file2, file1));
} finally {
FileUtil.purge(testDir, true);
}
}
public void testCopyDirectory() throws Exception {
try {
FileUtil.copyDirectory(new File("this.does.not.exist"), new File("dummy"));
assert false : "the source directory did not exist, this should have failed because of that";
} catch (Exception ok) {
}
// create a source directory and a destination directory. Make sure we start off
// with a non-existent destination directory - we want the copyDirectory to create it for us.
File outDir = FileUtil.createTempDirectory("fileUtilTestCopyDir", ".dest", null);
assert outDir.delete() : "failed to start out with a non-existent dest directory";
assert !outDir.exists() : "dest directory should not exist"; // yes, I am paranoid
File inDir = FileUtil.createTempDirectory("fileUtilTestCopyDir", ".src", null);
try {
// create some test files in our source directory
String testFilename0 = "file0.txt";
String testFilename1 = "subdir" + File.separatorChar + "subfile1.txt";
String testFilename2 = "subdir" + File.separatorChar + "subfile2.txt";
File testFile = new File(inDir, testFilename0);
StreamUtil.copy(new ByteArrayInputStream("0".getBytes()), new FileOutputStream(testFile));
assert "0".equals(new String(StreamUtil.slurp(new FileInputStream(testFile)))); // sanity check, make sure its there
testFile = new File(inDir, testFilename1);
testFile.getParentFile().mkdirs();
StreamUtil.copy(new ByteArrayInputStream("1".getBytes()), new FileOutputStream(testFile));
assert "1".equals(new String(StreamUtil.slurp(new FileInputStream(testFile)))); // sanity check, make sure its there
testFile = new File(inDir, testFilename2);
testFile.getParentFile().mkdirs();
StreamUtil.copy(new ByteArrayInputStream("2".getBytes()), new FileOutputStream(testFile));
assert "2".equals(new String(StreamUtil.slurp(new FileInputStream(testFile)))); // sanity check, make sure its there
// copy our source directory and confirm the copies are correct
FileUtil.copyDirectory(inDir, outDir);
testFile = new File(outDir, testFilename0);
assert testFile.exists() : "file did not get created: " + testFile;
assert "0".equals(new String(StreamUtil.slurp(new FileInputStream(testFile))));
testFile = new File(outDir, testFilename1);
assert testFile.exists() : "file did not get created: " + testFile;
assert "1".equals(new String(StreamUtil.slurp(new FileInputStream(testFile))));
testFile = new File(outDir, testFilename2);
assert testFile.exists() : "file did not get created: " + testFile;
assert "2".equals(new String(StreamUtil.slurp(new FileInputStream(testFile))));
// let's test getDirectoryFiles while we are here
List<File> outFiles = FileUtil.getDirectoryFiles(outDir);
assert outFiles != null : outFiles;
assert outFiles.size() == 3 : outFiles;
assert outFiles.contains(new File(testFilename0)) : outFiles;
assert outFiles.contains(new File(testFilename1)) : outFiles;
assert outFiles.contains(new File(testFilename2)) : outFiles;
} finally {
// clean up our test
try {
FileUtil.purge(inDir, true);
} catch (Exception ignore) {
}
try {
FileUtil.purge(outDir, true);
} catch (Exception ignore) {
}
}
}
public void testStripDriveLetter() {
StringBuilder str;
str = new StringBuilder("");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("");
str = new StringBuilder("\\");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("\\");
str = new StringBuilder("foo");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("foo");
str = new StringBuilder("foo\\bar");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("foo\\bar");
str = new StringBuilder("\\foo\\bar");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("\\foo\\bar");
str = new StringBuilder("C:");
assert FileUtil.stripDriveLetter(str).equals("C");
assert str.toString().equals("");
str = new StringBuilder("c:");
assert FileUtil.stripDriveLetter(str).equals("C");
assert str.toString().equals("");
str = new StringBuilder("C:\\");
assert FileUtil.stripDriveLetter(str).equals("C");
assert str.toString().equals("\\");
str = new StringBuilder("C:foo");
assert FileUtil.stripDriveLetter(str).equals("C");
assert str.toString().equals("foo");
str = new StringBuilder("C:foo\\bar");
assert FileUtil.stripDriveLetter(str).equals("C");
assert str.toString().equals("foo\\bar");
str = new StringBuilder("C:\\foo");
assert FileUtil.stripDriveLetter(str).equals("C");
assert str.toString().equals("\\foo");
str = new StringBuilder("C:\\foo\\bar");
assert FileUtil.stripDriveLetter(str).equals("C");
assert str.toString().equals("\\foo\\bar");
// test all the valid drive letters
String driveLetters = "abcdefghijklmnopqrstuvwxyz";
String testPath = "\\foo\\bar";
for (int i = 0; i < driveLetters.length(); i++) {
String lowerLetter = String.valueOf(driveLetters.charAt(i));
String upperLetter = String.valueOf(Character.toUpperCase(driveLetters.charAt(i)));
StringBuilder lowerPath = new StringBuilder(lowerLetter + ':' + testPath);
StringBuilder upperPath = new StringBuilder(upperLetter + ':' + testPath);
assert FileUtil.stripDriveLetter(lowerPath).equals(lowerLetter.toUpperCase());
assert lowerPath.toString().equals(testPath);
assert FileUtil.stripDriveLetter(upperPath).equals(upperLetter);
assert upperPath.toString().equals(testPath);
}
// unix paths should not be affected
str = new StringBuilder("/");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("/");
str = new StringBuilder("/foo");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("/foo");
str = new StringBuilder("foo/bar");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("foo/bar");
str = new StringBuilder("/foo/bar");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("/foo/bar");
str = new StringBuilder("hello:world/hello");
assert FileUtil.stripDriveLetter(str) == null;
assert str.toString().equals("hello:world/hello");
}
@Test
public void visitEachFileInDir() throws Exception {
File dir = new File(toFile(getClass().getResource(".")), "visit-each-file-in-dir");
deleteDirectory(dir);
dir.mkdirs();
File file1 = new File(dir, "file-1");
touch(file1);
File file2 = new File(dir, "file-2");
touch(file2);
List<File> expectedFiles = asList(file1, file2);
final List<File> actualFiles = new ArrayList<File>();
FileUtil.forEachFile(dir, new FileVisitor() {
@Override
public void visit(File file) {
actualFiles.add(file);
}
});
assertCollectionEqualsNoOrder(expectedFiles, actualFiles, "Expected to visit all files in directory");
}
@Test
public void visitFilesInSubdirectories() throws Exception {
File dir = new File(toFile(getClass().getResource(".")), "visit-files-in-sub-dirs");
deleteDirectory(dir);
dir.mkdirs();
File file1 = new File(dir, "file-1");
touch(file1);
File subdir1 = new File(dir, "subdir-1");
subdir1.mkdir();
File file2 = new File(subdir1, "file-2");
touch(file2);
File subdir2 = new File(dir, "subdir-2");
File file3 = new File(subdir2, "file-3");
touch(file3);
List<File> expectedFiles = asList(file1, file2, file3);
final List<File> actualFiles = new ArrayList<File>();
FileUtil.forEachFile(dir, new FileVisitor() {
@Override
public void visit(File file) {
actualFiles.add(file);
}
});
assertCollectionEqualsNoOrder(expectedFiles, actualFiles, "Expected to visit files in sub directories");
}
@Test
public void visitFilesInNestedSubDirectories() throws Exception {
File dir = new File(toFile(getClass().getResource(".")), "visit-files-in-nested-sub-dirs");
deleteDirectory(dir);
dir.mkdirs();
File file1 = new File(dir, "file-1");
touch(file1);
File subdir1 = new File(dir, "subdir-1");
subdir1.mkdir();
File file2 = new File(subdir1, "file-2");
touch(file2);
File subdir2 = new File(subdir1, "subdir-2");
File file3 = new File(subdir2, "file-3");
touch(file3);
List<File> expectedFiles = asList(file1, file2, file3);
final List<File> actualFiles = new ArrayList<File>();
FileUtil.forEachFile(dir, new FileVisitor() {
@Override
public void visit(File file) {
actualFiles.add(file);
}
});
assertCollectionEqualsNoOrder(expectedFiles, actualFiles, "Expected to visit files in nested sub directories");
}
public void testGetPattern() {
Pattern regex;
regex = assertPatternsRegex("(" + translateAbsoluteUnixPathToActualAsRegex("/basedir/(test1\\.txt)") + ")",
new PathFilter("/basedir", "test1.txt"));
assert regex.matcher(translateAbsoluteUnixPathToActual("/basedir/test1.txt")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedir/test2.txt")).matches();
regex = assertPatternsRegex("(" + translateAbsoluteUnixPathToActualAsRegex("/basedir/easy\\.txt") + ")|(" +
translateAbsoluteUnixPathToActualAsRegex("/basedir/test\\.txt") + ")", new PathFilter("/basedir/easy.txt",
null), new PathFilter("/basedir/test.txt", null));
assert regex.matcher(translateAbsoluteUnixPathToActual("/basedir/easy.txt")).matches();
assert regex.matcher(translateAbsoluteUnixPathToActual("/basedir/test.txt")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedir/easyXtxt")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedir/testXtxt")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedir/easy.txtX")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedir/test.txtX")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedirX/easy.txt")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedirX/test.txt")).matches();
assert !regex.matcher("easy.txt").matches() : "missing basedir";
assert !regex.matcher("test.txt").matches() : "missing basedir";
regex = assertPatternsRegex("(" + translateAbsoluteUnixPathToActualAsRegex("/basedir/([^/]*\\.txt)") + ")",
new PathFilter("/basedir", "*.txt"));
assert regex.matcher(translateAbsoluteUnixPathToActual("/basedir/foo.txt")).matches();
assert regex.matcher(translateAbsoluteUnixPathToActual("/basedir/file with spaces.txt")).matches();
assert regex.matcher(translateAbsoluteUnixPathToActual("/basedir/123.txt")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedir/subdir/foo.txt")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/basedir/foo.txt.swp")).matches();
regex = assertPatternsRegex("(" + translateAbsoluteUnixPathToActualAsRegex("/var/lib/([^/]*\\.war)") + ")|(" +
translateAbsoluteUnixPathToActualAsRegex("/var/lib/([^/]*\\.ear)") + ")", new PathFilter("/var/lib",
"*.war"), new PathFilter("/var/lib", "*.ear"));
assert regex.matcher(translateAbsoluteUnixPathToActual("/var/lib/myapp.war")).matches();
assert regex.matcher(translateAbsoluteUnixPathToActual("/var/lib/myapp.ear")).matches();
assert regex.matcher(translateAbsoluteUnixPathToActual("/var/lib/my-app.war")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/var/lib/myapp.War")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/var/libs/myapp.war")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("myapp.ear")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/var/lib/myapp.ear.rej")).matches();
regex = assertPatternsRegex("(" + translateAbsoluteUnixPathToActualAsRegex("/conf/(server-.\\.conf)") + ")",
new PathFilter("/conf", "server-?.conf"));
assert regex.matcher(translateAbsoluteUnixPathToActual("/conf/server-1.conf")).matches();
assert regex.matcher(translateAbsoluteUnixPathToActual("/conf/server-X.conf")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/conf/subconf/server-1.conf")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/conf/server.conf")).matches();
regex = assertPatternsRegex("(" + translateAbsoluteUnixPathToActualAsRegex("/etc/(.*[^/]*\\.conf)") + ")",
new PathFilter("/etc", "**/*.conf"));
assert regex.matcher(translateAbsoluteUnixPathToActual("/etc/yum.conf")).matches();
assert regex.matcher(translateAbsoluteUnixPathToActual("/etc/httpd/httpd.conf")).matches();
assert !regex.matcher(translateAbsoluteUnixPathToActual("/etc/foo.conf/foo")).matches();
}
public void testNormalizePath() throws Exception {
if (File.separatorChar == '\\') {
//windows
checkNormalization("\\\\server\\share\\bar", "\\\\server\\share\\path\\..\\bar");
//we just consider the ".." the name of the share of the UNC path
checkNormalization("\\\\server\\..\\bar", "\\\\server\\..\\bar");
checkNormalization(null, "\\\\server\\share\\..\\bar");
checkNormalization("C:\\bar", "C:\\foo\\..\\bar");
checkNormalization("C:\\bar", "c:\\foo\\..\\bar"); // make sure drive letter is normalized to upcase
checkNormalization(null, "C:\\..\\bar");
checkNormalization("\\foo", "/foo//");
checkNormalization("\\foo", "/foo/./");
checkNormalization("\\bar", "/foo/../bar");
checkNormalization("\\bar", "/foo/../bar/");
checkNormalization("\\baz", "/foo/../bar/../baz");
//we just consider the "." the name of the share of the UNC path
checkNormalization("\\\\foo\\.\\bar", "//foo//./bar");
checkNormalization(null, "/../");
checkNormalization(null, "../foo");
checkNormalization("foo", "foo/bar/..");
checkNormalization(null, "foo/../../bar");
checkNormalization("bar", "foo/../bar");
} else {
checkNormalization("/foo", "/foo//");
checkNormalization("/foo", "/foo/./");
checkNormalization("/bar", "/foo/../bar");
checkNormalization("/bar", "/foo/../bar/");
checkNormalization("/baz", "/foo/../bar/../baz");
checkNormalization("/foo/bar", "//foo//./bar");
checkNormalization(null, "/../");
checkNormalization(null, "../foo");
checkNormalization("foo", "foo/bar/..");
checkNormalization(null, "foo/../../bar");
checkNormalization("bar", "foo/../bar");
checkNormalization("~/bar", "~/foo/../bar/");
}
}
private void checkNormalization(String expectedResult, String path) {
File result = FileUtil.normalizePath(new File(path));
assert
expectedResult == null ? result == null : result != null && expectedResult.equals(result.getPath()) :
expectedResult + " failed. Should have been [" + expectedResult + "] but was [" + result + "]";
}
private Pattern assertPatternsRegex(String expectedPattern, PathFilter... filters) {
Pattern regex = FileUtil.generateRegex(asList(filters));
assert regex != null : "The regex was not able to be produced - it was null";
assert expectedPattern.equals(regex.pattern()) : "The expected pattern [" + expectedPattern
+ "] did not match the actual pattern [" + regex.pattern() + "]";
return regex;
}
private static String translateAbsoluteUnixPathToActualAsRegex(String path) {
return translateAbsoluteUnixPathToActual(path, true);
}
private static String translateAbsoluteUnixPathToActual(String path) {
return translateAbsoluteUnixPathToActual(path, false);
}
private static String translateAbsoluteUnixPathToActual(String path, boolean asRegex) {
if (File.separatorChar == '\\') {
//get the current drive letter
//leave out the trailing "\" - we have an absolute unix path on input, so we "use" the "/" of it
String driveLetter = new File(".").getAbsoluteFile().toPath().getRoot().toString().substring(0, 2);
path = driveLetter + path.replace("/", asRegex? "\\\\" : "\\");
}
return path;
}
}