/* * Copyright (C) 2007 The Guava Authors * * Licensed 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 com.google.common.io; import static com.google.common.io.Files.touch; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.hash.Hashing; import com.google.common.primitives.Bytes; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; import junit.framework.TestSuite; /** * Unit test for {@link Files}. * * @author Chris Nokleberg */ public class FilesTest extends IoTestCase { public static TestSuite suite() { TestSuite suite = new TestSuite(); suite.addTest(ByteSourceTester.tests("Files.asByteSource[File]", SourceSinkFactories.fileByteSourceFactory(), true)); suite.addTest(ByteSinkTester.tests("Files.asByteSink[File]", SourceSinkFactories.fileByteSinkFactory())); suite.addTest(ByteSinkTester.tests("Files.asByteSink[File, APPEND]", SourceSinkFactories.appendingFileByteSinkFactory())); suite.addTest(CharSourceTester.tests("Files.asCharSource[File, Charset]", SourceSinkFactories.fileCharSourceFactory(), false)); suite.addTest(CharSinkTester.tests("Files.asCharSink[File, Charset]", SourceSinkFactories.fileCharSinkFactory())); suite.addTest(CharSinkTester.tests("Files.asCharSink[File, Charset, APPEND]", SourceSinkFactories.appendingFileCharSinkFactory())); suite.addTestSuite(FilesTest.class); return suite; } public void testRoundTripSources() throws Exception { File asciiFile = getTestFile("ascii.txt"); ByteSource byteSource = Files.asByteSource(asciiFile); assertSame(byteSource, byteSource.asCharSource(Charsets.UTF_8).asByteSource(Charsets.UTF_8)); } public void testToByteArray() throws IOException { File asciiFile = getTestFile("ascii.txt"); File i18nFile = getTestFile("i18n.txt"); assertTrue(Arrays.equals(ASCII.getBytes(Charsets.US_ASCII), Files.toByteArray(asciiFile))); assertTrue(Arrays.equals(I18N.getBytes(Charsets.UTF_8), Files.toByteArray(i18nFile))); assertTrue(Arrays.equals(I18N.getBytes(Charsets.UTF_8), Files.asByteSource(i18nFile).read())); } public void testReadFile_withCorrectSize() throws IOException { File asciiFile = getTestFile("ascii.txt"); Closer closer = Closer.create(); try { InputStream in = closer.register(new FileInputStream(asciiFile)); byte[] bytes = Files.readFile(in, asciiFile.length()); assertTrue(Arrays.equals(ASCII.getBytes(Charsets.US_ASCII), bytes)); } catch (Throwable e) { throw closer.rethrow(e); } finally { closer.close(); } } public void testReadFile_withSmallerSize() throws IOException { File asciiFile = getTestFile("ascii.txt"); Closer closer = Closer.create(); try { InputStream in = closer.register(new FileInputStream(asciiFile)); byte[] bytes = Files.readFile(in, 10); assertTrue(Arrays.equals(ASCII.getBytes(Charsets.US_ASCII), bytes)); } catch (Throwable e) { throw closer.rethrow(e); } finally { closer.close(); } } public void testReadFile_withLargerSize() throws IOException { File asciiFile = getTestFile("ascii.txt"); Closer closer = Closer.create(); try { InputStream in = closer.register(new FileInputStream(asciiFile)); byte[] bytes = Files.readFile(in, 500); assertTrue(Arrays.equals(ASCII.getBytes(Charsets.US_ASCII), bytes)); } catch (Throwable e) { throw closer.rethrow(e); } finally { closer.close(); } } public void testReadFile_withSizeZero() throws IOException { File asciiFile = getTestFile("ascii.txt"); Closer closer = Closer.create(); try { InputStream in = closer.register(new FileInputStream(asciiFile)); byte[] bytes = Files.readFile(in, 0); assertTrue(Arrays.equals(ASCII.getBytes(Charsets.US_ASCII), bytes)); } catch (Throwable e) { throw closer.rethrow(e); } finally { closer.close(); } } /** * A {@link File} that provides a specialized value for {@link File#length()}. */ private static class BadLengthFile extends File { private final long badLength; public BadLengthFile(File delegate, long badLength) { super(delegate.getPath()); this.badLength = badLength; } @Override public long length() { return badLength; } private static final long serialVersionUID = 0; } public void testToString() throws IOException { File asciiFile = getTestFile("ascii.txt"); File i18nFile = getTestFile("i18n.txt"); assertEquals(ASCII, Files.toString(asciiFile, Charsets.US_ASCII)); assertEquals(I18N, Files.toString(i18nFile, Charsets.UTF_8)); assertThat(Files.toString(i18nFile, Charsets.US_ASCII)) .isNotEqualTo(I18N); } public void testWriteString() throws IOException { File temp = createTempFile(); Files.write(I18N, temp, Charsets.UTF_16LE); assertEquals(I18N, Files.toString(temp, Charsets.UTF_16LE)); } public void testWriteBytes() throws IOException { File temp = createTempFile(); byte[] data = newPreFilledByteArray(2000); Files.write(data, temp); assertTrue(Arrays.equals(data, Files.toByteArray(temp))); try { Files.write(null, temp); fail("expected exception"); } catch (NullPointerException expected) { } } public void testAppendString() throws IOException { File temp = createTempFile(); Files.append(I18N, temp, Charsets.UTF_16LE); assertEquals(I18N, Files.toString(temp, Charsets.UTF_16LE)); Files.append(I18N, temp, Charsets.UTF_16LE); assertEquals(I18N + I18N, Files.toString(temp, Charsets.UTF_16LE)); Files.append(I18N, temp, Charsets.UTF_16LE); assertEquals(I18N + I18N + I18N, Files.toString(temp, Charsets.UTF_16LE)); } public void testCopyToOutputStream() throws IOException { File i18nFile = getTestFile("i18n.txt"); ByteArrayOutputStream out = new ByteArrayOutputStream(); Files.copy(i18nFile, out); assertEquals(I18N, out.toString("UTF-8")); } public void testCopyToAppendable() throws IOException { File i18nFile = getTestFile("i18n.txt"); StringBuilder sb = new StringBuilder(); Files.copy(i18nFile, Charsets.UTF_8, sb); assertEquals(I18N, sb.toString()); } public void testCopyFile() throws IOException { File i18nFile = getTestFile("i18n.txt"); File temp = createTempFile(); Files.copy(i18nFile, temp); assertEquals(I18N, Files.toString(temp, Charsets.UTF_8)); } public void testCopyEqualFiles() throws IOException { File temp1 = createTempFile(); File temp2 = file(temp1.getPath()); assertEquals(temp1, temp2); Files.write(ASCII, temp1, Charsets.UTF_8); try { Files.copy(temp1, temp2); fail("Expected an IAE to be thrown but wasn't"); } catch (IllegalArgumentException expected) { } assertEquals(ASCII, Files.toString(temp1, Charsets.UTF_8)); } public void testCopySameFile() throws IOException { File temp = createTempFile(); Files.write(ASCII, temp, Charsets.UTF_8); try { Files.copy(temp, temp); fail("Expected an IAE to be thrown but wasn't"); } catch (IllegalArgumentException expected) { } assertEquals(ASCII, Files.toString(temp, Charsets.UTF_8)); } public void testCopyIdenticalFiles() throws IOException { File temp1 = createTempFile(); Files.write(ASCII, temp1, Charsets.UTF_8); File temp2 = createTempFile(); Files.write(ASCII, temp2, Charsets.UTF_8); Files.copy(temp1, temp2); assertEquals(ASCII, Files.toString(temp1, Charsets.UTF_8)); } public void testEqual() throws IOException { File asciiFile = getTestFile("ascii.txt"); File i18nFile = getTestFile("i18n.txt"); assertFalse(Files.equal(asciiFile, i18nFile)); assertTrue(Files.equal(asciiFile, asciiFile)); File temp = createTempFile(); Files.copy(asciiFile, temp); assertTrue(Files.equal(asciiFile, temp)); Files.copy(i18nFile, temp); assertTrue(Files.equal(i18nFile, temp)); Files.copy(asciiFile, temp); RandomAccessFile rf = new RandomAccessFile(temp, "rw"); rf.writeByte(0); rf.close(); assertEquals(asciiFile.length(), temp.length()); assertFalse(Files.equal(asciiFile, temp)); assertTrue(Files.asByteSource(asciiFile) .contentEquals(Files.asByteSource(asciiFile))); // 0-length files have special treatment (/proc, etc.) assertTrue(Files.equal(asciiFile, new BadLengthFile(asciiFile, 0))); } public void testNewReader() throws IOException { File asciiFile = getTestFile("ascii.txt"); try { Files.newReader(asciiFile, null); fail("expected exception"); } catch (NullPointerException expected) { } try { Files.newReader(null, Charsets.UTF_8); fail("expected exception"); } catch (NullPointerException expected) { } BufferedReader r = Files.newReader(asciiFile, Charsets.US_ASCII); try { assertEquals(ASCII, r.readLine()); } finally { r.close(); } } public void testNewWriter() throws IOException { File temp = createTempFile(); try { Files.newWriter(temp, null); fail("expected exception"); } catch (NullPointerException expected) { } try { Files.newWriter(null, Charsets.UTF_8); fail("expected exception"); } catch (NullPointerException expected) { } BufferedWriter w = Files.newWriter(temp, Charsets.UTF_8); try { w.write(I18N); } finally { w.close(); } File i18nFile = getTestFile("i18n.txt"); assertTrue(Files.equal(i18nFile, temp)); } public void testTouch() throws IOException { File temp = createTempFile(); assertTrue(temp.exists()); assertTrue(temp.delete()); assertFalse(temp.exists()); Files.touch(temp); assertTrue(temp.exists()); Files.touch(temp); assertTrue(temp.exists()); try { Files.touch(new File(temp.getPath()) { @Override public boolean setLastModified(long t) { return false; } private static final long serialVersionUID = 0; }); fail("expected exception"); } catch (IOException expected) { } } public void testTouchTime() throws IOException { File temp = createTempFile(); assertTrue(temp.exists()); temp.setLastModified(0); assertEquals(0, temp.lastModified()); Files.touch(temp); assertThat(temp.lastModified()).isNotEqualTo(0); } public void testCreateParentDirs_root() throws IOException { File file = root(); assertNull(file.getParentFile()); assertNull(file.getCanonicalFile().getParentFile()); Files.createParentDirs(file); } public void testCreateParentDirs_relativePath() throws IOException { File file = file("nonexistent.file"); assertNull(file.getParentFile()); assertNotNull(file.getCanonicalFile().getParentFile()); Files.createParentDirs(file); } public void testCreateParentDirs_noParentsNeeded() throws IOException { File file = file(getTempDir(), "nonexistent.file"); assertTrue(file.getParentFile().exists()); Files.createParentDirs(file); } public void testCreateParentDirs_oneParentNeeded() throws IOException { File file = file(getTempDir(), "parent", "nonexistent.file"); File parent = file.getParentFile(); assertFalse(parent.exists()); try { Files.createParentDirs(file); assertTrue(parent.exists()); } finally { assertTrue(parent.delete()); } } public void testCreateParentDirs_multipleParentsNeeded() throws IOException { File file = file(getTempDir(), "grandparent", "parent", "nonexistent.file"); File parent = file.getParentFile(); File grandparent = parent.getParentFile(); assertFalse(grandparent.exists()); Files.createParentDirs(file); assertTrue(parent.exists()); } public void testCreateParentDirs_nonDirectoryParentExists() throws IOException { File parent = getTestFile("ascii.txt"); assertTrue(parent.isFile()); File file = file(parent, "foo"); try { Files.createParentDirs(file); fail(); } catch (IOException expected) { } } public void testCreateTempDir() { File temp = Files.createTempDir(); assertTrue(temp.exists()); assertTrue(temp.isDirectory()); assertThat(temp.listFiles()).isEmpty(); assertTrue(temp.delete()); } public void testMove() throws IOException { File i18nFile = getTestFile("i18n.txt"); File temp1 = createTempFile(); File temp2 = createTempFile(); Files.copy(i18nFile, temp1); moveHelper(true, temp1, temp2); assertTrue(Files.equal(temp2, i18nFile)); } public void testMoveViaCopy() throws IOException { File i18nFile = getTestFile("i18n.txt"); File temp1 = createTempFile(); File temp2 = createTempFile(); Files.copy(i18nFile, temp1); moveHelper(true, new UnmovableFile(temp1, false, true), temp2); assertTrue(Files.equal(temp2, i18nFile)); } public void testMoveFailures() throws IOException { File temp1 = createTempFile(); File temp2 = createTempFile(); moveHelper(false, new UnmovableFile(temp1, false, false), temp2); moveHelper(false, new UnmovableFile(temp1, false, false), new UnmovableFile(temp2, true, false)); try { File asciiFile = getTestFile("ascii.txt"); moveHelper(false, asciiFile, asciiFile); fail("expected exception"); } catch (IllegalArgumentException expected) { } } private void moveHelper(boolean success, File from, File to) throws IOException { try { Files.move(from, to); if (success) { assertFalse(from.exists()); assertTrue(to.exists()); } else { fail("expected exception"); } } catch (IOException possiblyExpected) { if (success) { throw possiblyExpected; } } } private static class UnmovableFile extends File { private final boolean canRename; private final boolean canDelete; public UnmovableFile(File file, boolean canRename, boolean canDelete) { super(file.getPath()); this.canRename = canRename; this.canDelete = canDelete; } @Override public boolean renameTo(File to) { return canRename && super.renameTo(to); } @Override public boolean delete() { return canDelete && super.delete(); } private static final long serialVersionUID = 0; } public void testLineReading() throws IOException { File temp = createTempFile(); assertNull(Files.readFirstLine(temp, Charsets.UTF_8)); assertTrue(Files.readLines(temp, Charsets.UTF_8).isEmpty()); PrintWriter w = new PrintWriter(Files.newWriter(temp, Charsets.UTF_8)); w.println("hello"); w.println(""); w.println(" world "); w.println(""); w.close(); assertEquals("hello", Files.readFirstLine(temp, Charsets.UTF_8)); assertEquals(ImmutableList.of("hello", "", " world ", ""), Files.readLines(temp, Charsets.UTF_8)); assertTrue(temp.delete()); } public void testReadLines_withLineProcessor() throws IOException { File temp = createTempFile(); LineProcessor<List<String>> collect = new LineProcessor<List<String>>() { List<String> collector = new ArrayList<String>(); @Override public boolean processLine(String line) { collector.add(line); return true; } @Override public List<String> getResult() { return collector; } }; assertThat(Files.readLines(temp, Charsets.UTF_8, collect)).isEmpty(); PrintWriter w = new PrintWriter(Files.newWriter(temp, Charsets.UTF_8)); w.println("hello"); w.println(""); w.println(" world "); w.println(""); w.close(); Files.readLines(temp, Charsets.UTF_8, collect); assertThat(collect.getResult()) .containsExactly("hello", "", " world ", "").inOrder(); LineProcessor<List<String>> collectNonEmptyLines = new LineProcessor<List<String>>() { List<String> collector = new ArrayList<String>(); @Override public boolean processLine(String line) { if (line.length() > 0) { collector.add(line); } return true; } @Override public List<String> getResult() { return collector; } }; Files.readLines(temp, Charsets.UTF_8, collectNonEmptyLines); assertThat(collectNonEmptyLines.getResult()).containsExactly( "hello", " world ").inOrder(); assertTrue(temp.delete()); } public void testHash() throws IOException { File asciiFile = getTestFile("ascii.txt"); File i18nFile = getTestFile("i18n.txt"); String init = "d41d8cd98f00b204e9800998ecf8427e"; assertEquals(init, Hashing.md5().newHasher().hash().toString()); String asciiHash = "e5df5a39f2b8cb71b24e1d8038f93131"; assertEquals(asciiHash, Files.hash(asciiFile, Hashing.md5()).toString()); String i18nHash = "7fa826962ce2079c8334cd4ebf33aea4"; assertEquals(i18nHash, Files.hash(i18nFile, Hashing.md5()).toString()); } public void testMap() throws IOException { // Test data int size = 1024; byte[] bytes = newPreFilledByteArray(size); // Setup File file = createTempFile(); Files.write(bytes, file); // Test MappedByteBuffer actual = Files.map(file); // Verify ByteBuffer expected = ByteBuffer.wrap(bytes); assertTrue("ByteBuffers should be equal.", expected.equals(actual)); } public void testMap_noSuchFile() throws IOException { // Setup File file = createTempFile(); boolean deleted = file.delete(); assertTrue(deleted); // Test try { Files.map(file); fail("Should have thrown FileNotFoundException."); } catch (FileNotFoundException expected) { } } public void testMap_readWrite() throws IOException { // Test data int size = 1024; byte[] expectedBytes = new byte[size]; byte[] bytes = newPreFilledByteArray(1024); // Setup File file = createTempFile(); Files.write(bytes, file); Random random = new Random(); random.nextBytes(expectedBytes); // Test MappedByteBuffer map = Files.map(file, MapMode.READ_WRITE); map.put(expectedBytes); // Verify byte[] actualBytes = Files.toByteArray(file); assertTrue(Arrays.equals(expectedBytes, actualBytes)); } public void testMap_readWrite_creates() throws IOException { // Test data int size = 1024; byte[] expectedBytes = newPreFilledByteArray(1024); // Setup File file = createTempFile(); boolean deleted = file.delete(); assertTrue(deleted); assertFalse(file.exists()); // Test MappedByteBuffer map = Files.map(file, MapMode.READ_WRITE, size); map.put(expectedBytes); // Verify assertTrue(file.exists()); assertTrue(file.isFile()); assertEquals(size, file.length()); byte[] actualBytes = Files.toByteArray(file); assertTrue(Arrays.equals(expectedBytes, actualBytes)); } public void testMap_readWrite_max_value_plus_1() throws IOException { // Setup File file = createTempFile(); // Test try { Files.map(file, MapMode.READ_WRITE, (long) Integer.MAX_VALUE + 1); fail("Should throw when size exceeds Integer.MAX_VALUE"); } catch (IllegalArgumentException expected) { } } public void testGetFileExtension() { assertEquals("txt", Files.getFileExtension(".txt")); assertEquals("txt", Files.getFileExtension("blah.txt")); assertEquals("txt", Files.getFileExtension("blah..txt")); assertEquals("txt", Files.getFileExtension(".blah.txt")); assertEquals("txt", Files.getFileExtension("/tmp/blah.txt")); assertEquals("gz", Files.getFileExtension("blah.tar.gz")); assertEquals("", Files.getFileExtension("/")); assertEquals("", Files.getFileExtension(".")); assertEquals("", Files.getFileExtension("..")); assertEquals("", Files.getFileExtension("...")); assertEquals("", Files.getFileExtension("blah")); assertEquals("", Files.getFileExtension("blah.")); assertEquals("", Files.getFileExtension(".blah.")); assertEquals("", Files.getFileExtension("/foo.bar/blah")); assertEquals("", Files.getFileExtension("/foo/.bar/blah")); } public void testGetNameWithoutExtension() { assertEquals("", Files.getNameWithoutExtension(".txt")); assertEquals("blah", Files.getNameWithoutExtension("blah.txt")); assertEquals("blah.", Files.getNameWithoutExtension("blah..txt")); assertEquals(".blah", Files.getNameWithoutExtension(".blah.txt")); assertEquals("blah", Files.getNameWithoutExtension("/tmp/blah.txt")); assertEquals("blah.tar", Files.getNameWithoutExtension("blah.tar.gz")); assertEquals("", Files.getNameWithoutExtension("/")); assertEquals("", Files.getNameWithoutExtension(".")); assertEquals(".", Files.getNameWithoutExtension("..")); assertEquals("..", Files.getNameWithoutExtension("...")); assertEquals("blah", Files.getNameWithoutExtension("blah")); assertEquals("blah", Files.getNameWithoutExtension("blah.")); assertEquals(".blah", Files.getNameWithoutExtension(".blah.")); assertEquals("blah", Files.getNameWithoutExtension("/foo.bar/blah")); assertEquals("blah", Files.getNameWithoutExtension("/foo/.bar/blah")); } public void testReadBytes() throws IOException { ByteProcessor<byte[]> processor = new ByteProcessor<byte[]>() { private final ByteArrayOutputStream out = new ByteArrayOutputStream(); @Override public boolean processBytes(byte[] buffer, int offset, int length) throws IOException { if (length >= 0) { out.write(buffer, offset, length); } return true; } @Override public byte[] getResult() { return out.toByteArray(); } }; File asciiFile = getTestFile("ascii.txt"); byte[] result = Files.readBytes(asciiFile, processor); assertEquals(Bytes.asList(Files.toByteArray(asciiFile)), Bytes.asList(result)); } public void testReadBytes_returnFalse() throws IOException { ByteProcessor<byte[]> processor = new ByteProcessor<byte[]>() { private final ByteArrayOutputStream out = new ByteArrayOutputStream(); @Override public boolean processBytes(byte[] buffer, int offset, int length) throws IOException { if (length > 0) { out.write(buffer, offset, 1); return false; } else { return true; } } @Override public byte[] getResult() { return out.toByteArray(); } }; File asciiFile = getTestFile("ascii.txt"); byte[] result = Files.readBytes(asciiFile, processor); assertEquals(1, result.length); } public void testPredicates() throws IOException { File asciiFile = getTestFile("ascii.txt"); File dir = asciiFile.getParentFile(); assertTrue(Files.isDirectory().apply(dir)); assertFalse(Files.isFile().apply(dir)); assertFalse(Files.isDirectory().apply(asciiFile)); assertTrue(Files.isFile().apply(asciiFile)); } /** * Returns a root path for the file system. */ private static File root() { return File.listRoots()[0]; } /** * Returns a {@code File} object for the given path parts. */ private static File file(String first, String... more) { return file(new File(first), more); } /** * Returns a {@code File} object for the given path parts. */ private static File file(File first, String... more) { // not very efficient, but should definitely be correct File file = first; for (String name : more) { file = new File(file, name); } return file; } }