/************************************************************************** OmegaT - Computer Assisted Translation (CAT) tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. Copyright (C) 2015 Aaron Madlon-Kay, Alex Buloichik Home page: http://www.omegat.org/ Support center: http://groups.yahoo.com/group/OmegaT/ This file is part of OmegaT. OmegaT is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OmegaT 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 for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. **************************************************************************/ package org.omegat.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.io.UncheckedIOException; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystemLoopException; import java.nio.file.Files; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; import java.util.stream.IntStream; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.omegat.util.FileUtil.ICollisionCallback; /** * @author Aaron Madlon-Kay * @author Alex Buloichik */ public class FileUtilTest { private File base; @Before public final void setUp() throws Exception { base = Files.createTempDirectory("omegat").toFile(); } @After public final void tearDown() throws Exception { FileUtils.deleteDirectory(base); } @Test public void testCopyFilesTo() throws Exception { File targetDir = makeDir(base, "target"); // Make structure to copy into targetDir File sourceDir = makeDir(base, "source"); File file1 = writeFile(new File(sourceDir, "file1"), "file1-first"); File file2 = writeFile(new File(sourceDir, "sub1/file2"), "file2-first"); // Copy all files. Make sure they are identical. FileUtil.copyFilesTo(targetDir, sourceDir.listFiles(), null); final File file1trg = new File(targetDir, "file1"); final File sub1trg = new File(targetDir, "sub1"); final File file2trg = new File(targetDir, "sub1/file2"); assertTrue(fileContentsAreEqual(file1, file1trg)); assertTrue(new File(targetDir, "sub1").isDirectory()); assertTrue(fileContentsAreEqual(file2, file2trg)); // Modify source files. File file3 = new File(sourceDir, "file3"); writeFile(file3, "file3-first"); writeFile(file1, "file1-second"); writeFile(file2, "file2-second"); // Do copy but don't overwrite anything. Only file3 should be copied. FileUtil.copyFilesTo(targetDir, sourceDir.listFiles(), new ICollisionCallback() { @Override public boolean shouldReplace(File file, int thisFile, int totalFiles) { return false; } @Override public boolean isCanceled() { return false; } }); File file3trg = new File(targetDir, "file3"); assertFalse(fileContentsAreEqual(file1, file1trg)); assertFalse(fileContentsAreEqual(file2, file2trg)); assertTrue(fileContentsAreEqual(file3, file3trg)); // Add sub1/file4 to target; this will disappear after replacing sub1. File file4trg = writeFile(new File(targetDir, "sub1/file4"), "file4"); // Do copy and overwrite sub1 only. sub1/file2 should be replaced // and sub1/file4 should disappear. FileUtil.copyFilesTo(targetDir, sourceDir.listFiles(), new ICollisionCallback() { @Override public boolean shouldReplace(File file, int thisFile, int totalFiles) { return file.equals(sub1trg); } @Override public boolean isCanceled() { return false; } }); assertFalse(fileContentsAreEqual(file1, file1trg)); assertTrue(fileContentsAreEqual(file2, file2trg)); assertTrue(fileContentsAreEqual(file3, file3trg)); assertFalse(file4trg.exists()); // Do copy and cancel on last file. None of the files should be changed. // shouldReplace() should be called once for all files in source/. CountingCallback callback = new CountingCallback() { private boolean isCanceled = false; @Override public boolean shouldReplace(File file, int thisFile, int totalFiles) { calledTimes++; isCanceled = thisFile + 1 == totalFiles; return !isCanceled(); } @Override public boolean isCanceled() { return isCanceled; } }; FileUtil.copyFilesTo(targetDir, sourceDir.listFiles(), callback); assertEquals(sourceDir.listFiles().length, callback.calledTimes); assertFalse(fileContentsAreEqual(file1, file1trg)); assertTrue(fileContentsAreEqual(file2, file2trg)); assertTrue(fileContentsAreEqual(file3, file3trg)); // Try copying to non-existent destination. File newTarget = new File(base, "newtarget"); FileUtil.copyFilesTo(newTarget, sourceDir.listFiles(), null); assertTrue(newTarget.isDirectory()); assertTrue(fileContentsAreEqual(file1, new File(newTarget, "file1"))); // Try copying to destination that exists but is a file. File targetFile = writeFile(new File(base, "targetFile"), ""); try { FileUtil.copyFilesTo(targetFile, sourceDir.listFiles(), null); fail("copyFilesTo should fail when target dir is a file."); } catch (IOException ex) { } } private abstract class CountingCallback implements ICollisionCallback { int calledTimes = 0; } private File makeDir(File parent, String... names) { File dir = parent; for (String name : names) { dir = new File(dir, name); } assertTrue(dir.mkdirs()); return dir; } private File writeFile(File file, String content) throws FileNotFoundException, UnsupportedEncodingException { File dir = file.getParentFile(); if (!dir.isDirectory()) { assertTrue(dir.mkdirs()); } PrintStream stream = new PrintStream(file, StandardCharsets.US_ASCII.name()); stream.println(content); stream.close(); return file; } private boolean fileContentsAreEqual(File file1, File file2) throws IOException { return Arrays.equals(FileUtils.readFileToByteArray(file1), FileUtils.readFileToByteArray(file2)); } @Test public void testRelative() throws Exception { assertFalse(FileUtil.isRelative("C:\\zz")); assertFalse(FileUtil.isRelative("z:/zz")); assertFalse(FileUtil.isRelative("c:\\zz")); assertFalse(FileUtil.isRelative("z:/zz")); assertTrue(FileUtil.isRelative("1:/zz")); assertFalse(FileUtil.isRelative("/zz")); assertFalse(FileUtil.isRelative("\\zz")); assertTrue(FileUtil.isRelative("zz/")); } @Test public void testAbsoluteForSystem() throws Exception { assertEquals("C:/zzz", FileUtil.absoluteForSystem("C:\\zzz", Platform.OsType.WIN64)); assertEquals("/zzz", FileUtil.absoluteForSystem("C:\\zzz", Platform.OsType.LINUX64)); assertEquals("/zzz", FileUtil.absoluteForSystem("C:\\zzz", Platform.OsType.MAC64)); assertEquals("/zzz", FileUtil.absoluteForSystem("\\zzz", Platform.OsType.WIN64)); assertEquals("/zzz", FileUtil.absoluteForSystem("\\zzz", Platform.OsType.LINUX64)); assertEquals("/zzz", FileUtil.absoluteForSystem("\\zzz", Platform.OsType.MAC64)); } @Test public void testEOL() throws Exception { File dir = new File("build/testdata/"); dir.mkdirs(); File in = new File(dir, "in.eol"); File out = new File(dir, "out.eol"); byte[] eoln = "12\n34\n56\n".getBytes("UTF-8"); byte[] eolr = "12\r34\r56\r".getBytes("UTF-8"); byte[] eolrn = "12\r\n34\r\n56\r\n".getBytes("UTF-8"); byte[][] eols = new byte[][] { eoln, eolr, eolrn }; FileUtils.writeByteArrayToFile(out, eoln); assertEquals("\n", FileUtil.getEOL(out, StandardCharsets.UTF_8)); for (byte[] eolfrom : eols) { FileUtils.writeByteArrayToFile(in, eolfrom); FileUtil.copyFileWithEolConversion(in, out, StandardCharsets.UTF_8); assertEquals("\n", FileUtil.getEOL(out, StandardCharsets.UTF_8)); } FileUtils.writeByteArrayToFile(out, eolr); assertEquals("\r", FileUtil.getEOL(out, StandardCharsets.UTF_8)); for (byte[] eolfrom : eols) { FileUtils.writeByteArrayToFile(in, eolfrom); FileUtil.copyFileWithEolConversion(in, out, StandardCharsets.UTF_8); assertEquals("\r", FileUtil.getEOL(out, StandardCharsets.UTF_8)); } FileUtils.writeByteArrayToFile(out, eolrn); assertEquals("\r\n", FileUtil.getEOL(out, StandardCharsets.UTF_8)); for (byte[] eolfrom : eols) { FileUtils.writeByteArrayToFile(in, eolfrom); FileUtil.copyFileWithEolConversion(in, out, StandardCharsets.UTF_8); assertEquals("\r\n", FileUtil.getEOL(out, StandardCharsets.UTF_8)); } } @Test public void testDeleteTree() throws Exception { // /root File root = new File(base, "root"); // /root/sub File sub = new File(root, "sub"); assertTrue(sub.mkdirs()); // /root2 File root2 = new File(base, "root2"); assertTrue(root2.mkdirs()); File file = new File(root2, "file"); assertTrue(file.createNewFile()); try { // /root/sub/subsub -> /root2 Files.createSymbolicLink(new File(sub, "subsub").toPath(), root2.toPath()); } catch (UnsupportedOperationException | IOException ex) { // Creating symlinks not supported on this system } // Can't delete /root yet because it's not empty assertFalse(root.delete()); FileUtils.deleteDirectory(root); assertFalse(root.exists()); // Make sure we didn't follow the symlink assertTrue(file.exists()); } @Test public void testCompileFileMask() { Pattern r = FileUtil.compileFileMask("Ab1-&*/**"); assertEquals("(?:|.*/)Ab1\\-\\&[^/]*(?:|/.*)", r.pattern()); } @Test public void testFilePatterns() { // From // https://confluence.atlassian.com/fisheye/pattern-matching-guide-298976797.html String p = "*.txt"; assertTrue(patternMatches(p, "/foo.txt")); assertTrue(patternMatches(p, "/bar/foo.txt")); assertFalse(patternMatches(p, "/foo.txty")); assertFalse(patternMatches(p, "/bar/foo.txty/")); p = "/*.txt"; assertTrue(patternMatches(p, "/foo.txt")); assertFalse(patternMatches(p, "/bar/foo.txt")); p = "dir1/file.txt"; assertTrue(patternMatches(p, "/dir1/file.txt")); assertTrue(patternMatches(p, "/dir3/dir1/file.txt")); assertTrue(patternMatches(p, "/dir3/dir2/dir1/file.txt")); p = "**/dir1/file.txt"; assertTrue(patternMatches(p, "/dir1/file.txt")); assertTrue(patternMatches(p, "/dir3/dir1/file.txt")); assertTrue(patternMatches(p, "/dir3/dir2/dir1/file.txt")); p = "/**/dir1/file.txt"; assertTrue(patternMatches(p, "/dir1/file.txt")); assertTrue(patternMatches(p, "/dir3/dir1/file.txt")); assertTrue(patternMatches(p, "/dir3/dir2/dir1/file.txt")); p = "/dir3/**/dir1/file.txt"; assertTrue(patternMatches(p, "/dir3/dir1/file.txt")); assertTrue(patternMatches(p, "/dir3/dir2/dir1/file.txt")); assertFalse(patternMatches(p, "/dir3/file.txt")); assertFalse(patternMatches(p, "/dir1/file.txt")); p = "/dir1/**"; assertTrue(patternMatches(p, "/dir1/foo")); assertTrue(patternMatches(p, "/dir1/foo/bar")); assertTrue(patternMatches(p, "/dir1/foo.baz")); assertFalse(patternMatches(p, "/dir11/foo")); p = "/dir1*"; assertTrue(patternMatches(p, "/dir11")); assertTrue(patternMatches(p, "/dir12")); assertTrue(patternMatches(p, "/dir12345")); assertFalse(patternMatches(p, "/dir1/dir2")); p = "/dir??"; assertTrue(patternMatches(p, "/dir11")); assertTrue(patternMatches(p, "/dir12")); assertFalse(patternMatches(p, "/dir12345")); // From https://ant.apache.org/manual/dirtasks.html#patterns p = "*.java"; assertTrue(patternMatches(p, ".java")); assertTrue(patternMatches(p, "x.java")); assertTrue(patternMatches(p, "FooBar.java")); assertFalse(patternMatches(p, "FooBar.xml")); p = "?.java"; assertTrue(patternMatches(p, "x.java")); assertTrue(patternMatches(p, "A.java")); assertFalse(patternMatches(p, ".java")); assertFalse(patternMatches(p, "xyz.java")); p = "/?abc/*/*.java"; assertTrue(patternMatches(p, "/xabc/foobar/test.java")); p = "/test/**"; assertTrue(patternMatches(p, "/test/x.java")); assertTrue(patternMatches(p, "/test/foo/bar/xyz.html")); assertFalse(patternMatches(p, "/xyz.xml")); assertEquals(FileUtil.compileFileMask("mypackage/test/**").pattern(), FileUtil.compileFileMask("mypackage/test/").pattern()); p = "**/CVS/*"; assertTrue(patternMatches(p, "CVS/Repository")); assertTrue(patternMatches(p, "org/apache/CVS/Entries")); assertTrue(patternMatches(p, "org/apache/jakarta/tools/ant/CVS/Entries")); assertFalse(patternMatches(p, "org/apache/CVS/foo/bar/Entries")); p = "org/apache/jakarta/**"; assertTrue(patternMatches(p, "org/apache/jakarta/tools/ant/docs/index.html")); assertTrue(patternMatches(p, "org/apache/jakarta/test.xml")); assertFalse(patternMatches(p, "org/apache/xyz.java")); p = "org/apache/**/CVS/*"; assertTrue(patternMatches(p, "org/apache/CVS/Entries")); assertTrue(patternMatches(p, "org/apache/jakarta/tools/ant/CVS/Entries")); assertFalse(patternMatches(p, "org/apache/CVS/foo/bar/Entries")); // Ant docs claim this pattern "Matches all files that have a test // element in their path, including test as a filename." p = "**/test/**"; assertTrue(patternMatches(p, "test")); assertTrue(patternMatches(p, "/test")); assertTrue(patternMatches(p, "foo/test")); assertTrue(patternMatches(p, "/foo/test")); assertTrue(patternMatches(p, "foo/test/bar")); assertTrue(patternMatches(p, "/foo/test/bar")); assertFalse(patternMatches(p, "/foo/tests/bar")); assertFalse(patternMatches(p, "/foo/tests.bar")); p = "foo/**/bar"; assertFalse(patternMatches(p, "foobar")); assertFalse(patternMatches(p, "foobaz/bar")); } private static boolean patternMatches(String pattern, String path) { Pattern p = FileUtil.compileFileMask(pattern); return p.matcher(path).matches(); } @Test public void testBuildFileList() throws Exception { File tempDir = Files.createTempDirectory("omegat").toFile(); assertTrue(tempDir.isDirectory()); File subDir = new File(tempDir, "a"); assertTrue(subDir.mkdirs()); File aFile = new File(subDir, "foo"); assertTrue(aFile.createNewFile()); aFile = new File(subDir, "bar"); assertTrue(aFile.createNewFile()); List<File> list1 = FileUtil.buildFileList(tempDir, false); assertTrue(list1.isEmpty()); List<File> list2 = FileUtil.buildFileList(tempDir, true); assertEquals(2, list2.size()); Collections.sort(list2); assertTrue(list2.get(0).getPath().endsWith("bar")); try { File lnk = new File(tempDir, "hoge"); Files.createSymbolicLink(lnk.toPath(), subDir.toPath()); List<File> list3 = FileUtil.buildFileList(lnk, true); List<File> list4 = FileUtil.buildFileList(subDir, true); assertEquals(list3.size(), list4.size()); assertTrue(IntStream.range(0, list3.size()).allMatch(i -> { try { return list3.get(i).getCanonicalFile().equals(list4.get(i).getCanonicalFile()); } catch (IOException e) { throw new UncheckedIOException(e); } })); } catch (UnsupportedOperationException | IOException ex) { // Creating symbolic links appears to not be supported on this // system } try { Files.createSymbolicLink(new File(tempDir, "baz").toPath(), tempDir.toPath()); FileUtil.buildFileList(tempDir, true); fail("Should die from file system loop"); } catch (UnsupportedOperationException | IOException ex) { // Creating symbolic links appears to not be supported on this // system } catch (UncheckedIOException ex) { if (!(ex.getCause() instanceof FileSystemLoopException)) { throw ex; } // Creating symbolic links appears to not be supported on this // system } FileUtils.deleteDirectory(tempDir); } }