blob: 9379208d30c2478633af59b1cfddcabd42dcd0b9 [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* 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.jimfs;
import static com.google.common.jimfs.TestUtils.bytes;
import static com.google.common.jimfs.TestUtils.permutations;
import static com.google.common.jimfs.TestUtils.preFilledBytes;
import static com.google.common.primitives.Bytes.concat;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardCopyOption.ATOMIC_MOVE;
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.DSYNC;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.SPARSE;
import static java.nio.file.StandardOpenOption.SYNC;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.Assert.assertArrayEquals;
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 com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Ordering;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.primitives.Bytes;
import com.google.common.util.concurrent.Uninterruptibles;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.NonReadableChannelException;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.ClosedDirectoryStreamException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.nio.file.NotLinkException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SecureDirectoryStream;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.PatternSyntaxException;
/**
* Tests an in-memory file system through the public APIs in {@link Files}, etc. This also acts as
* the tests for {@code FileSystemView}, as each public API method is (mostly) implemented by a
* method in {@code FileSystemView}.
*
* <p>These tests uses a Unix-like file system, but most of what they test applies to any file
* system configuration.
*
* @author Colin Decker
*/
@RunWith(JUnit4.class)
public class JimfsUnixLikeFileSystemTest extends AbstractJimfsIntegrationTest {
private static final Configuration UNIX_CONFIGURATION =
Configuration.unix()
.toBuilder()
.setAttributeViews("basic", "owner", "posix", "unix")
.setMaxSize(1024 * 1024 * 1024) // 1 GB
.setMaxCacheSize(256 * 1024 * 1024) // 256 MB
.build();
@Override
protected FileSystem createFileSystem() {
return Jimfs.newFileSystem("unix", UNIX_CONFIGURATION);
}
@Test
public void testFileSystem() {
assertThat(fs.getSeparator()).isEqualTo("/");
assertThat(fs.getRootDirectories())
.containsExactlyElementsIn(ImmutableSet.of(path("/")))
.inOrder();
assertThat(fs.isOpen()).isTrue();
assertThat(fs.isReadOnly()).isFalse();
assertThat(fs.supportedFileAttributeViews()).containsExactly("basic", "owner", "posix", "unix");
assertThat(fs.provider()).isInstanceOf(JimfsFileSystemProvider.class);
}
@Test
public void testFileStore() throws IOException {
FileStore fileStore = Iterables.getOnlyElement(fs.getFileStores());
assertThat(fileStore.name()).isEqualTo("jimfs");
assertThat(fileStore.type()).isEqualTo("jimfs");
assertThat(fileStore.isReadOnly()).isFalse();
long totalSpace = 1024 * 1024 * 1024; // 1 GB
assertThat(fileStore.getTotalSpace()).isEqualTo(totalSpace);
assertThat(fileStore.getUnallocatedSpace()).isEqualTo(totalSpace);
assertThat(fileStore.getUsableSpace()).isEqualTo(totalSpace);
Files.write(fs.getPath("/foo"), new byte[10000]);
assertThat(fileStore.getTotalSpace()).isEqualTo(totalSpace);
// We wrote 10000 bytes, but since the file system allocates fixed size blocks, more than 10k
// bytes may have been allocated. As such, the unallocated space after the write can be at most
// maxUnallocatedSpace.
assertThat(fileStore.getUnallocatedSpace() <= totalSpace - 10000).isTrue();
// Usable space is at most unallocated space. (In this case, it's currently exactly unallocated
// space, but that's not required.)
assertThat(fileStore.getUsableSpace() <= fileStore.getUnallocatedSpace()).isTrue();
Files.delete(fs.getPath("/foo"));
assertThat(fileStore.getTotalSpace()).isEqualTo(totalSpace);
assertThat(fileStore.getUnallocatedSpace()).isEqualTo(totalSpace);
assertThat(fileStore.getUsableSpace()).isEqualTo(totalSpace);
}
@Test
public void testPaths() {
assertThatPath("/").isAbsolute()
.and().hasRootComponent("/")
.and().hasNoNameComponents();
assertThatPath("foo").isRelative()
.and().hasNameComponents("foo");
assertThatPath("foo/bar").isRelative()
.and().hasNameComponents("foo", "bar");
assertThatPath("/foo/bar/baz").isAbsolute()
.and().hasRootComponent("/")
.and().hasNameComponents("foo", "bar", "baz");
}
@Test
public void testPaths_equalityIsCaseSensitive() {
assertThatPath("foo").isNotEqualTo(path("FOO"));
}
@Test
public void testPaths_areSortedCaseSensitive() {
Path p1 = path("a");
Path p2 = path("B");
Path p3 = path("c");
Path p4 = path("D");
assertThat(Ordering.natural().immutableSortedCopy(Arrays.asList(p3, p4, p1, p2)))
.isEqualTo(ImmutableList.of(p2, p4, p1, p3));
// would be p1, p2, p3, p4 if sorting were case insensitive
}
@Test
public void testPaths_resolve() {
assertThatPath(path("/").resolve("foo/bar")).isAbsolute()
.and().hasRootComponent("/")
.and().hasNameComponents("foo", "bar");
assertThatPath(path("foo/bar").resolveSibling("baz")).isRelative()
.and().hasNameComponents("foo", "baz");
assertThatPath(path("foo/bar").resolve("/one/two")).isAbsolute()
.and().hasRootComponent("/")
.and().hasNameComponents("one", "two");
}
@Test
public void testPaths_normalize() {
assertThatPath(path("foo/bar/..").normalize()).isRelative()
.and().hasNameComponents("foo");
assertThatPath(path("foo/./bar/../baz/test/./../stuff").normalize()).isRelative()
.and().hasNameComponents("foo", "baz", "stuff");
assertThatPath(path("../../foo/./bar").normalize()).isRelative()
.and().hasNameComponents("..", "..", "foo", "bar");
assertThatPath(path("foo/../../bar").normalize()).isRelative()
.and().hasNameComponents("..", "bar");
assertThatPath(path(".././..").normalize()).isRelative()
.and().hasNameComponents("..", "..");
}
@Test
public void testPaths_relativize() {
assertThatPath(path("/foo/bar").relativize(path("/foo/bar/baz"))).isRelative()
.and().hasNameComponents("baz");
assertThatPath(path("/foo/bar/baz").relativize(path("/foo/bar"))).isRelative()
.and().hasNameComponents("..");
assertThatPath(path("/foo/bar/baz").relativize(path("/foo/baz/bar"))).isRelative()
.and().hasNameComponents("..", "..", "baz", "bar");
assertThatPath(path("foo/bar").relativize(path("foo"))).isRelative()
.and().hasNameComponents("..");
assertThatPath(path("foo").relativize(path("foo/bar"))).isRelative()
.and().hasNameComponents("bar");
try {
path("/foo/bar").relativize(path("bar"));
fail();
} catch (IllegalArgumentException expected) {
}
try {
path("bar").relativize(path("/foo/bar"));
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testPaths_startsWith_endsWith() {
assertThat(path("/foo/bar").startsWith("/")).isTrue();
assertThat(path("/foo/bar").startsWith("/foo")).isTrue();
assertThat(path("/foo/bar").startsWith("/foo/bar")).isTrue();
assertThat(path("/foo/bar").endsWith("bar")).isTrue();
assertThat(path("/foo/bar").endsWith("foo/bar")).isTrue();
assertThat(path("/foo/bar").endsWith("/foo/bar")).isTrue();
assertThat(path("/foo/bar").endsWith("/foo")).isFalse();
assertThat(path("/foo/bar").startsWith("foo/bar")).isFalse();
}
@Test
public void testPaths_toAbsolutePath() {
assertThatPath(path("/foo/bar").toAbsolutePath()).isAbsolute()
.and().hasRootComponent("/")
.and().hasNameComponents("foo", "bar")
.and().isEqualTo(path("/foo/bar"));
assertThatPath(path("foo/bar").toAbsolutePath()).isAbsolute()
.and().hasRootComponent("/")
.and().hasNameComponents("work", "foo", "bar")
.and().isEqualTo(path("/work/foo/bar"));
}
@Test
public void testPaths_toRealPath() throws IOException {
Files.createDirectories(path("/foo/bar"));
Files.createSymbolicLink(path("/link"), path("/"));
assertThatPath(path("/link/foo/bar").toRealPath()).isEqualTo(path("/foo/bar"));
assertThatPath(path("").toRealPath()).isEqualTo(path("/work"));
assertThatPath(path(".").toRealPath()).isEqualTo(path("/work"));
assertThatPath(path("..").toRealPath()).isEqualTo(path("/"));
assertThatPath(path("../..").toRealPath()).isEqualTo(path("/"));
assertThatPath(path("./.././..").toRealPath()).isEqualTo(path("/"));
assertThatPath(path("./.././../.").toRealPath()).isEqualTo(path("/"));
}
@Test
public void testPaths_toUri() {
assertThat(path("/").toUri()).isEqualTo(URI.create("jimfs://unix/"));
assertThat(path("/foo").toUri()).isEqualTo(URI.create("jimfs://unix/foo"));
assertThat(path("/foo/bar").toUri()).isEqualTo(URI.create("jimfs://unix/foo/bar"));
assertThat(path("foo").toUri()).isEqualTo(URI.create("jimfs://unix/work/foo"));
assertThat(path("foo/bar").toUri()).isEqualTo(URI.create("jimfs://unix/work/foo/bar"));
assertThat(path("").toUri()).isEqualTo(URI.create("jimfs://unix/work/"));
assertThat(path("./../.").toUri()).isEqualTo(URI.create("jimfs://unix/work/./.././"));
}
@Test
public void testPaths_getFromUri() {
assertThatPath(Paths.get(URI.create("jimfs://unix/"))).isEqualTo(path("/"));
assertThatPath(Paths.get(URI.create("jimfs://unix/foo"))).isEqualTo(path("/foo"));
assertThatPath(Paths.get(URI.create("jimfs://unix/foo%20bar"))).isEqualTo(path("/foo bar"));
assertThatPath(Paths.get(URI.create("jimfs://unix/foo/./bar"))).isEqualTo(path("/foo/./bar"));
assertThatPath(Paths.get(URI.create("jimfs://unix/foo/bar/"))).isEqualTo(path("/foo/bar"));
}
@Test
public void testPathMatchers_regex() {
assertThatPath("bar").matches("regex:.*");
assertThatPath("bar").matches("regex:bar");
assertThatPath("bar").matches("regex:[a-z]+");
assertThatPath("/foo/bar").matches("regex:/.*");
assertThatPath("/foo/bar").matches("regex:/.*/bar");
}
@Test
public void testPathMatchers_glob() {
assertThatPath("bar").matches("glob:bar");
assertThatPath("bar").matches("glob:*");
assertThatPath("/foo").doesNotMatch("glob:*");
assertThatPath("/foo/bar").doesNotMatch("glob:*");
assertThatPath("/foo/bar").matches("glob:**");
assertThatPath("/foo/bar").matches("glob:/**");
assertThatPath("foo/bar").doesNotMatch("glob:/**");
assertThatPath("/foo/bar/baz/stuff").matches("glob:/foo/**");
assertThatPath("/foo/bar/baz/stuff").matches("glob:/**/stuff");
assertThatPath("/foo").matches("glob:/[a-z]*");
assertThatPath("/Foo").doesNotMatch("glob:/[a-z]*");
assertThatPath("/foo/bar/baz/Stuff.java").matches("glob:**/*.java");
assertThatPath("/foo/bar/baz/Stuff.java").matches("glob:**/*.{java,class}");
assertThatPath("/foo/bar/baz/Stuff.class").matches("glob:**/*.{java,class}");
assertThatPath("/foo/bar/baz/Stuff.java").matches("glob:**/*.*");
try {
fs.getPathMatcher("glob:**/*.{java,class");
fail();
} catch (PatternSyntaxException expected) {
}
}
@Test
public void testPathMatchers_invalid() {
try {
fs.getPathMatcher("glob");
fail();
} catch (IllegalArgumentException expected) {
}
try {
fs.getPathMatcher("foo:foo");
fail();
} catch (UnsupportedOperationException expected) {
assertThat(expected.getMessage()).contains("syntax");
}
}
@Test
public void testNewFileSystem_hasRootAndWorkingDirectory() throws IOException {
assertThatPath("/").hasChildren("work");
assertThatPath("/work").hasNoChildren();
}
@Test
public void testCreateDirectory_absolute() throws IOException {
Files.createDirectory(path("/test"));
assertThatPath("/test").exists();
assertThatPath("/").hasChildren("test", "work");
Files.createDirectory(path("/foo"));
Files.createDirectory(path("/foo/bar"));
assertThatPath("/foo/bar").exists();
assertThatPath("/foo").hasChildren("bar");
}
@Test
public void testCreateFile_absolute() throws IOException {
Files.createFile(path("/test.txt"));
assertThatPath("/test.txt").isRegularFile();
assertThatPath("/").hasChildren("test.txt", "work");
Files.createDirectory(path("/foo"));
Files.createFile(path("/foo/test.txt"));
assertThatPath("/foo/test.txt").isRegularFile();
assertThatPath("/foo").hasChildren("test.txt");
}
@Test
public void testCreateSymbolicLink_absolute() throws IOException {
Files.createSymbolicLink(path("/link.txt"), path("test.txt"));
assertThatPath("/link.txt", NOFOLLOW_LINKS).isSymbolicLink().withTarget("test.txt");
assertThatPath("/").hasChildren("link.txt", "work");
Files.createDirectory(path("/foo"));
Files.createSymbolicLink(path("/foo/link.txt"), path("test.txt"));
assertThatPath("/foo/link.txt")
.noFollowLinks()
.isSymbolicLink()
.withTarget("test.txt");
assertThatPath("/foo").hasChildren("link.txt");
}
@Test
public void testCreateLink_absolute() throws IOException {
Files.createFile(path("/test.txt"));
Files.createLink(path("/link.txt"), path("/test.txt"));
// don't assert that the link is the same file here, just that it was created
// later tests check that linking works correctly
assertThatPath("/link.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("/").hasChildren("link.txt", "test.txt", "work");
Files.createDirectory(path("/foo"));
Files.createLink(path("/foo/link.txt"), path("/test.txt"));
assertThatPath("/foo/link.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("/foo").hasChildren("link.txt");
}
@Test
public void testCreateDirectory_relative() throws IOException {
Files.createDirectory(path("test"));
assertThatPath("/work/test", NOFOLLOW_LINKS).isDirectory();
assertThatPath("test", NOFOLLOW_LINKS).isDirectory();
assertThatPath("/work").hasChildren("test");
assertThatPath("test").isSameFileAs("/work/test");
Files.createDirectory(path("foo"));
Files.createDirectory(path("foo/bar"));
assertThatPath("/work/foo/bar", NOFOLLOW_LINKS).isDirectory();
assertThatPath("foo/bar", NOFOLLOW_LINKS).isDirectory();
assertThatPath("/work/foo").hasChildren("bar");
assertThatPath("foo").hasChildren("bar");
assertThatPath("foo/bar").isSameFileAs("/work/foo/bar");
}
@Test
public void testCreateFile_relative() throws IOException {
Files.createFile(path("test.txt"));
assertThatPath("/work/test.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("test.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("/work").hasChildren("test.txt");
assertThatPath("test.txt").isSameFileAs("/work/test.txt");
Files.createDirectory(path("foo"));
Files.createFile(path("foo/test.txt"));
assertThatPath("/work/foo/test.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("foo/test.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("/work/foo").hasChildren("test.txt");
assertThatPath("foo").hasChildren("test.txt");
assertThatPath("foo/test.txt").isSameFileAs("/work/foo/test.txt");
}
@Test
public void testCreateSymbolicLink_relative() throws IOException {
Files.createSymbolicLink(path("link.txt"), path("test.txt"));
assertThatPath("/work/link.txt", NOFOLLOW_LINKS).isSymbolicLink().withTarget("test.txt");
assertThatPath("link.txt", NOFOLLOW_LINKS).isSymbolicLink().withTarget("test.txt");
assertThatPath("/work").hasChildren("link.txt");
Files.createDirectory(path("foo"));
Files.createSymbolicLink(path("foo/link.txt"), path("test.txt"));
assertThatPath("/work/foo/link.txt", NOFOLLOW_LINKS).isSymbolicLink().withTarget("test.txt");
assertThatPath("foo/link.txt", NOFOLLOW_LINKS).isSymbolicLink().withTarget("test.txt");
assertThatPath("/work/foo").hasChildren("link.txt");
assertThatPath("foo").hasChildren("link.txt");
}
@Test
public void testCreateLink_relative() throws IOException {
Files.createFile(path("test.txt"));
Files.createLink(path("link.txt"), path("test.txt"));
// don't assert that the link is the same file here, just that it was created
// later tests check that linking works correctly
assertThatPath("/work/link.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("link.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("/work").hasChildren("link.txt", "test.txt");
Files.createDirectory(path("foo"));
Files.createLink(path("foo/link.txt"), path("test.txt"));
assertThatPath("/work/foo/link.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("foo/link.txt", NOFOLLOW_LINKS).isRegularFile();
assertThatPath("foo").hasChildren("link.txt");
}
@Test
public void testCreateFile_existing() throws IOException {
Files.createFile(path("/test"));
try {
Files.createFile(path("/test"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/test", expected.getMessage());
}
try {
Files.createDirectory(path("/test"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/test", expected.getMessage());
}
try {
Files.createSymbolicLink(path("/test"), path("/foo"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/test", expected.getMessage());
}
Files.createFile(path("/foo"));
try {
Files.createLink(path("/test"), path("/foo"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/test", expected.getMessage());
}
}
@Test
public void testCreateFile_parentDoesNotExist() throws IOException {
try {
Files.createFile(path("/foo/test"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/foo/test", expected.getMessage());
}
try {
Files.createDirectory(path("/foo/test"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/foo/test", expected.getMessage());
}
try {
Files.createSymbolicLink(path("/foo/test"), path("/bar"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/foo/test", expected.getMessage());
}
Files.createFile(path("/bar"));
try {
Files.createLink(path("/foo/test"), path("/bar"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/foo/test", expected.getMessage());
}
}
@Test
public void testCreateFile_parentIsNotDirectory() throws IOException {
Files.createDirectory(path("/foo"));
Files.createFile(path("/foo/bar"));
try {
Files.createFile(path("/foo/bar/baz"));
fail();
} catch (NoSuchFileException expected) {
assertThat(expected.getFile()).isEqualTo("/foo/bar/baz");
}
}
@Test
public void testCreateFile_nonDirectoryHigherInPath() throws IOException {
Files.createDirectory(path("/foo"));
Files.createFile(path("/foo/bar"));
try {
Files.createFile(path("/foo/bar/baz/stuff"));
fail();
} catch (NoSuchFileException expected) {
assertThat(expected.getFile()).isEqualTo("/foo/bar/baz/stuff");
}
}
@Test
public void testCreateFile_parentSymlinkDoesNotExist() throws IOException {
Files.createDirectory(path("/foo"));
Files.createSymbolicLink(path("/foo/bar"), path("/foo/nope"));
try {
Files.createFile(path("/foo/bar/baz"));
fail();
} catch (NoSuchFileException expected) {
assertThat(expected.getFile()).isEqualTo("/foo/bar/baz");
}
}
@Test
public void testCreateFile_symlinkHigherInPathDoesNotExist() throws IOException {
Files.createDirectory(path("/foo"));
Files.createSymbolicLink(path("/foo/bar"), path("nope"));
try {
Files.createFile(path("/foo/bar/baz/stuff"));
fail();
} catch (NoSuchFileException expected) {
assertThat(expected.getFile()).isEqualTo("/foo/bar/baz/stuff");
}
}
@Test
public void testCreateFile_parentSymlinkDoesPointsToNonDirectory() throws IOException {
Files.createDirectory(path("/foo"));
Files.createFile(path("/foo/file"));
Files.createSymbolicLink(path("/foo/bar"), path("/foo/file"));
try {
Files.createFile(path("/foo/bar/baz"));
fail();
} catch (NoSuchFileException expected) {
assertThat(expected.getFile()).isEqualTo("/foo/bar/baz");
}
}
@Test
public void testCreateFile_symlinkHigherInPathPointsToNonDirectory() throws IOException {
Files.createDirectory(path("/foo"));
Files.createFile(path("/foo/file"));
Files.createSymbolicLink(path("/foo/bar"), path("file"));
try {
Files.createFile(path("/foo/bar/baz/stuff"));
fail();
} catch (NoSuchFileException expected) {
assertThat(expected.getFile()).isEqualTo("/foo/bar/baz/stuff");
}
}
@Test
public void testCreateFile_withInitialAttributes() throws IOException {
Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rwxrwxrwx");
FileAttribute<?> permissionsAttr = PosixFilePermissions.asFileAttribute(permissions);
Files.createFile(path("/normal"));
Files.createFile(path("/foo"), permissionsAttr);
assertThatPath("/normal").attribute("posix:permissions").isNot(permissions);
assertThatPath("/foo").attribute("posix:permissions").is(permissions);
FileAttribute<UserPrincipal> ownerAttr =
new BasicFileAttribute<>(
"posix:owner", fs.getUserPrincipalLookupService().lookupPrincipalByName("foo"));
Files.createFile(path("/foo2"), ownerAttr, permissionsAttr);
assertThatPath("/normal").attribute("owner:owner").isNot(ownerAttr.value());
assertThatPath("/foo2").attribute("owner:owner").is(ownerAttr.value());
assertThatPath("/foo2").attribute("posix:permissions").is(permissions);
}
@Test
public void testCreateFile_withInitialAttributes_illegalInitialAttribute() throws IOException {
try {
Files.createFile(
path("/foo"),
new BasicFileAttribute<>("basic:lastModifiedTime", FileTime.fromMillis(0L)));
fail();
} catch (UnsupportedOperationException expected) {
}
assertThatPath("/foo").doesNotExist();
try {
Files.createFile(path("/foo"), new BasicFileAttribute<>("basic:noSuchAttribute", "foo"));
fail();
} catch (IllegalArgumentException expected) {
}
assertThatPath("/foo").doesNotExist();
}
@Test
public void testOpenChannel_withInitialAttributes_createNewFile() throws IOException {
FileAttribute<Set<PosixFilePermission>> permissions =
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxrwxrwx"));
Files.newByteChannel(path("/foo"), ImmutableSet.of(WRITE, CREATE), permissions).close();
assertThatPath("/foo").isRegularFile()
.and().attribute("posix:permissions").is(permissions.value());
}
@Test
public void testOpenChannel_withInitialAttributes_fileExists() throws IOException {
Files.createFile(path("/foo"));
FileAttribute<Set<PosixFilePermission>> permissions =
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxrwxrwx"));
Files.newByteChannel(path("/foo"), ImmutableSet.of(WRITE, CREATE), permissions).close();
assertThatPath("/foo").isRegularFile()
.and().attribute("posix:permissions").isNot(permissions.value());
}
@Test
public void testCreateDirectory_withInitialAttributes() throws IOException {
FileAttribute<Set<PosixFilePermission>> permissions =
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxrwxrwx"));
Files.createDirectory(path("/foo"), permissions);
assertThatPath("/foo").isDirectory()
.and().attribute("posix:permissions").is(permissions.value());
Files.createDirectory(path("/normal"));
assertThatPath("/normal").isDirectory()
.and().attribute("posix:permissions").isNot(permissions.value());
}
@Test
public void testCreateSymbolicLink_withInitialAttributes() throws IOException {
FileAttribute<Set<PosixFilePermission>> permissions =
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxrwxrwx"));
Files.createSymbolicLink(path("/foo"), path("bar"), permissions);
assertThatPath("/foo", NOFOLLOW_LINKS).isSymbolicLink()
.and().attribute("posix:permissions").is(permissions.value());
Files.createSymbolicLink(path("/normal"), path("bar"));
assertThatPath("/normal", NOFOLLOW_LINKS).isSymbolicLink()
.and().attribute("posix:permissions").isNot(permissions.value());
}
@Test
public void testCreateDirectories() throws IOException {
Files.createDirectories(path("/foo/bar/baz"));
assertThatPath("/foo").isDirectory();
assertThatPath("/foo/bar").isDirectory();
assertThatPath("/foo/bar/baz").isDirectory();
Files.createDirectories(path("/foo/asdf/jkl"));
assertThatPath("/foo/asdf").isDirectory();
assertThatPath("/foo/asdf/jkl").isDirectory();
Files.createDirectories(path("bar/baz"));
assertThatPath("bar/baz").isDirectory();
assertThatPath("/work/bar/baz").isDirectory();
}
@Test
public void testDirectories_newlyCreatedDirectoryHasTwoLinks() throws IOException {
// one link from its parent to it; one from it to itself
Files.createDirectory(path("/foo"));
assertThatPath("/foo").hasLinkCount(2);
}
@Test
public void testDirectories_creatingDirectoryAddsOneLinkToParent() throws IOException {
// from the .. direntry
Files.createDirectory(path("/foo"));
Files.createDirectory(path("/foo/bar"));
assertThatPath("/foo").hasLinkCount(3);
Files.createDirectory(path("/foo/baz"));
assertThatPath("/foo").hasLinkCount(4);
}
@Test
public void testDirectories_creatingNonDirectoryDoesNotAddLinkToParent() throws IOException {
Files.createDirectory(path("/foo"));
Files.createFile(path("/foo/file"));
Files.createSymbolicLink(path("/foo/fileSymlink"), path("file"));
Files.createLink(path("/foo/link"), path("/foo/file"));
Files.createSymbolicLink(path("/foo/fooSymlink"), path("/foo"));
assertThatPath("/foo").hasLinkCount(2);
}
@Test
public void testSize_forNewFile_isZero() throws IOException {
Files.createFile(path("/test"));
assertThatPath("/test").hasSize(0);
}
@Test
public void testRead_forNewFile_isEmpty() throws IOException {
Files.createFile(path("/test"));
assertThatPath("/test").containsNoBytes();
}
@Test
public void testWriteFile_succeeds() throws IOException {
Files.createFile(path("/test"));
Files.write(path("/test"), new byte[] {0, 1, 2, 3});
}
@Test
public void testSize_forFileAfterWrite_isNumberOfBytesWritten() throws IOException {
Files.write(path("/test"), new byte[] {0, 1, 2, 3});
assertThatPath("/test").hasSize(4);
}
@Test
public void testRead_forFileAfterWrite_isBytesWritten() throws IOException {
byte[] bytes = {0, 1, 2, 3};
Files.write(path("/test"), bytes);
assertThatPath("/test").containsBytes(bytes);
}
@Test
public void testWriteFile_withStandardOptions() throws IOException {
Path test = path("/test");
byte[] bytes = {0, 1, 2, 3};
try {
// CREATE and CREATE_NEW not specified
Files.write(test, bytes, WRITE);
fail();
} catch (NoSuchFileException expected) {
assertEquals(test.toString(), expected.getMessage());
}
Files.write(test, bytes, CREATE_NEW); // succeeds, file does not exist
assertThatPath("/test").containsBytes(bytes);
try {
Files.write(test, bytes, CREATE_NEW); // CREATE_NEW requires file not exist
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals(test.toString(), expected.getMessage());
}
Files.write(test, new byte[] {4, 5}, CREATE); // succeeds, ok for file to already exist
assertThatPath("/test").containsBytes(4, 5, 2, 3); // did not truncate or append, so overwrote
Files.write(test, bytes, WRITE, CREATE, TRUNCATE_EXISTING); // default options
assertThatPath("/test").containsBytes(bytes);
Files.write(test, bytes, WRITE, APPEND);
assertThatPath("/test").containsBytes(0, 1, 2, 3, 0, 1, 2, 3);
Files.write(test, bytes, WRITE, CREATE, TRUNCATE_EXISTING, APPEND, SPARSE, DSYNC, SYNC);
assertThatPath("/test").containsBytes(bytes);
try {
Files.write(test, bytes, READ, WRITE); // READ not allowed
fail();
} catch (UnsupportedOperationException expected) {
}
}
@Test
public void testWriteLines_succeeds() throws IOException {
Files.write(path("/test.txt"), ImmutableList.of("hello", "world"), UTF_8);
}
@Test
public void testOpenFile_withReadAndTruncateExisting_doesNotTruncateFile() throws IOException {
byte[] bytes = bytes(1, 2, 3, 4);
Files.write(path("/test"), bytes);
try (FileChannel channel = FileChannel.open(path("/test"), READ, TRUNCATE_EXISTING)) {
// TRUNCATE_EXISTING ignored when opening for read
byte[] readBytes = new byte[4];
channel.read(ByteBuffer.wrap(readBytes));
assertThat(Bytes.asList(readBytes)).isEqualTo(Bytes.asList(bytes));
}
}
@Test
public void testRead_forFileAfterWriteLines_isLinesWritten() throws IOException {
Files.write(path("/test.txt"), ImmutableList.of("hello", "world"), UTF_8);
assertThatPath("/test.txt").containsLines("hello", "world");
}
@Test
public void testWriteLines_withStandardOptions() throws IOException {
Path test = path("/test.txt");
ImmutableList<String> lines = ImmutableList.of("hello", "world");
try {
// CREATE and CREATE_NEW not specified
Files.write(test, lines, UTF_8, WRITE);
fail();
} catch (NoSuchFileException expected) {
assertEquals(test.toString(), expected.getMessage());
}
Files.write(test, lines, UTF_8, CREATE_NEW); // succeeds, file does not exist
assertThatPath(test).containsLines(lines);
try {
Files.write(test, lines, UTF_8, CREATE_NEW); // CREATE_NEW requires file not exist
fail();
} catch (FileAlreadyExistsException expected) {
}
// succeeds, ok for file to already exist
Files.write(test, ImmutableList.of("foo"), UTF_8, CREATE);
// did not truncate or append, so overwrote
if (System.getProperty("line.separator").length() == 2) {
// on Windows, an extra character is overwritten by the \r\n line separator
assertThatPath(test).containsLines("foo", "", "world");
} else {
assertThatPath(test).containsLines("foo", "o", "world");
}
Files.write(test, lines, UTF_8, WRITE, CREATE, TRUNCATE_EXISTING); // default options
assertThatPath(test).containsLines(lines);
Files.write(test, lines, UTF_8, WRITE, APPEND);
assertThatPath(test).containsLines("hello", "world", "hello", "world");
Files.write(test, lines, UTF_8, WRITE, CREATE, TRUNCATE_EXISTING, APPEND, SPARSE, DSYNC, SYNC);
assertThatPath(test).containsLines(lines);
try {
Files.write(test, lines, UTF_8, READ, WRITE); // READ not allowed
fail();
} catch (UnsupportedOperationException expected) {
}
}
@Test
public void testWrite_fileExistsButIsNotRegularFile() throws IOException {
Files.createDirectory(path("/foo"));
try {
// non-CREATE mode
Files.write(path("/foo"), preFilledBytes(10), WRITE);
fail();
} catch (FileSystemException expected) {
assertThat(expected.getFile()).isEqualTo("/foo");
assertThat(expected.getMessage()).contains("regular file");
}
try {
// CREATE mode
Files.write(path("/foo"), preFilledBytes(10));
fail();
} catch (FileSystemException expected) {
assertThat(expected.getFile()).isEqualTo("/foo");
assertThat(expected.getMessage()).contains("regular file");
}
}
@Test
public void testDelete_file() throws IOException {
try {
Files.delete(path("/test"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/test", expected.getMessage());
}
try {
Files.delete(path("/foo/bar"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/foo/bar", expected.getMessage());
}
assertFalse(Files.deleteIfExists(path("/test")));
assertFalse(Files.deleteIfExists(path("/foo/bar")));
Files.createFile(path("/test"));
assertThatPath("/test").isRegularFile();
Files.delete(path("/test"));
assertThatPath("/test").doesNotExist();
Files.createFile(path("/test"));
assertTrue(Files.deleteIfExists(path("/test")));
assertThatPath("/test").doesNotExist();
}
@Test
public void testDelete_file_whenOpenReferencesRemain() throws IOException {
// the open streams should continue to function normally despite the deletion
Path foo = path("/foo");
byte[] bytes = preFilledBytes(100);
Files.write(foo, bytes);
InputStream in = Files.newInputStream(foo);
OutputStream out = Files.newOutputStream(foo, APPEND);
FileChannel channel = FileChannel.open(foo, READ, WRITE);
assertThat(channel.size()).isEqualTo(100L);
Files.delete(foo);
assertThatPath("/foo").doesNotExist();
assertThat(channel.size()).isEqualTo(100L);
ByteBuffer buf = ByteBuffer.allocate(100);
while (buf.hasRemaining()) {
channel.read(buf);
}
assertArrayEquals(bytes, buf.array());
byte[] moreBytes = {1, 2, 3, 4, 5};
out.write(moreBytes);
assertThat(channel.size()).isEqualTo(105L);
buf.clear();
assertThat(channel.read(buf)).isEqualTo(5);
buf.flip();
byte[] b = new byte[5];
buf.get(b);
assertArrayEquals(moreBytes, b);
byte[] allBytes = new byte[105];
int off = 0;
int read;
while ((read = in.read(allBytes, off, allBytes.length - off)) != -1) {
off += read;
}
assertArrayEquals(concat(bytes, moreBytes), allBytes);
channel.close();
out.close();
in.close();
}
@Test
public void testDelete_directory() throws IOException {
Files.createDirectories(path("/foo/bar"));
assertThatPath("/foo").isDirectory();
assertThatPath("/foo/bar").isDirectory();
Files.delete(path("/foo/bar"));
assertThatPath("/foo/bar").doesNotExist();
assertTrue(Files.deleteIfExists(path("/foo")));
assertThatPath("/foo").doesNotExist();
}
@Test
public void testDelete_pathPermutations() throws IOException {
Path bar = path("/work/foo/bar");
Files.createDirectories(bar);
for (Path path : permutations(bar)) {
Files.createDirectories(bar);
assertThatPath(path).isSameFileAs(bar);
Files.delete(path);
assertThatPath(bar).doesNotExist();
assertThatPath(path).doesNotExist();
}
Path baz = path("/test/baz");
Files.createDirectories(baz);
Path hello = baz.resolve("hello.txt");
for (Path path : permutations(hello)) {
Files.createFile(hello);
assertThatPath(path).isSameFileAs(hello);
Files.delete(path);
assertThatPath(hello).doesNotExist();
assertThatPath(path).doesNotExist();
}
}
@Test
public void testDelete_directory_cantDeleteNonEmptyDirectory() throws IOException {
Files.createDirectories(path("/foo/bar"));
try {
Files.delete(path("/foo"));
fail();
} catch (DirectoryNotEmptyException expected) {
assertThat(expected.getFile()).isEqualTo("/foo");
}
try {
Files.deleteIfExists(path("/foo"));
fail();
} catch (DirectoryNotEmptyException expected) {
assertThat(expected.getFile()).isEqualTo("/foo");
}
}
@Test
public void testDelete_directory_canDeleteWorkingDirectoryByAbsolutePath() throws IOException {
assertThatPath("/work").exists();
assertThatPath("").exists();
assertThatPath(".").exists();
Files.delete(path("/work"));
assertThatPath("/work").doesNotExist();
assertThatPath("").exists();
assertThatPath(".").exists();
}
@Test
public void testDelete_directory_cantDeleteWorkingDirectoryByRelativePath() throws IOException {
try {
Files.delete(path(""));
fail();
} catch (FileSystemException expected) {
assertThat(expected.getFile()).isEqualTo("");
}
try {
Files.delete(path("."));
fail();
} catch (FileSystemException expected) {
assertThat(expected.getFile()).isEqualTo(".");
}
try {
Files.delete(path("../../work"));
fail();
} catch (FileSystemException expected) {
assertThat(expected.getFile()).isEqualTo("../../work");
}
try {
Files.delete(path("./../work/.././../work/."));
fail();
} catch (FileSystemException expected) {
assertThat(expected.getFile()).isEqualTo("./../work/.././../work/.");
}
}
@Test
public void testDelete_directory_cantDeleteRoot() throws IOException {
// delete working directory so that root is empty
// don't want to just be testing the "can't delete when not empty" logic
Files.delete(path("/work"));
try {
Files.delete(path("/"));
fail();
} catch (IOException expected) {
assertThat(expected.getMessage()).contains("root");
}
Files.createDirectories(path("/foo/bar"));
try {
Files.delete(path("/foo/bar/../.."));
fail();
} catch (IOException expected) {
assertThat(expected.getMessage()).contains("root");
}
try {
Files.delete(path("/foo/./../foo/bar/./../bar/.././../../.."));
fail();
} catch (IOException expected) {
assertThat(expected.getMessage()).contains("root");
}
}
@Test
public void testSymbolicLinks() throws IOException {
Files.createSymbolicLink(path("/link.txt"), path("/file.txt"));
assertThatPath("/link.txt", NOFOLLOW_LINKS).isSymbolicLink().withTarget("/file.txt");
assertThatPath("/link.txt").doesNotExist(); // following the link; target doesn't exist
try {
Files.createFile(path("/link.txt"));
fail();
} catch (FileAlreadyExistsException expected) {
}
try {
Files.readAllBytes(path("/link.txt"));
fail();
} catch (NoSuchFileException expected) {
}
Files.createFile(path("/file.txt"));
assertThatPath("/link.txt").isRegularFile(); // following the link; target does exist
assertThatPath("/link.txt").containsNoBytes();
Files.createSymbolicLink(path("/foo"), path("/bar/baz"));
assertThatPath("/foo", NOFOLLOW_LINKS).isSymbolicLink().withTarget("/bar/baz");
assertThatPath("/foo").doesNotExist(); // following the link; target doesn't exist
Files.createDirectories(path("/bar/baz"));
assertThatPath("/foo").isDirectory(); // following the link; target does exist
Files.createFile(path("/bar/baz/test.txt"));
assertThatPath("/foo/test.txt", NOFOLLOW_LINKS).isRegularFile(); // follow intermediate link
try {
Files.readSymbolicLink(path("/none"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/none", expected.getMessage());
}
try {
Files.readSymbolicLink(path("/file.txt"));
fail();
} catch (NotLinkException expected) {
assertEquals("/file.txt", expected.getMessage());
}
}
@Test
public void testSymbolicLinks_symlinkCycle() throws IOException {
Files.createDirectory(path("/foo"));
Files.createSymbolicLink(path("/foo/bar"), path("baz"));
Files.createSymbolicLink(path("/foo/baz"), path("bar"));
try {
Files.createFile(path("/foo/bar/file"));
fail();
} catch (IOException expected) {
assertThat(expected.getMessage()).contains("symbolic link");
}
try {
Files.write(path("/foo/bar"), preFilledBytes(10));
fail();
} catch (IOException expected) {
assertThat(expected.getMessage()).contains("symbolic link");
}
}
@Test
public void testSymbolicLinks_lookupOfAbsoluteSymlinkPathFromRelativePath() throws IOException {
// relative path lookups are in the FileSystemView for the working directory
// this tests that when an absolute path is encountered, the lookup handles it correctly
Files.createDirectories(path("/foo/bar/baz"));
Files.createFile(path("/foo/bar/baz/file"));
Files.createDirectories(path("one/two/three"));
Files.createSymbolicLink(path("/work/one/two/three/link"), path("/foo/bar"));
assertThatPath("one/two/three/link/baz/file").isSameFileAs("/foo/bar/baz/file");
}
@Test
public void testLink() throws IOException {
Files.createFile(path("/file.txt"));
// checking link count requires "unix" attribute support, which we're using here
assertThatPath("/file.txt").hasLinkCount(1);
Files.createLink(path("/link.txt"), path("/file.txt"));
assertThatPath("/link.txt").isSameFileAs("/file.txt");
assertThatPath("/file.txt").hasLinkCount(2);
assertThatPath("/link.txt").hasLinkCount(2);
assertThatPath("/file.txt").containsNoBytes();
assertThatPath("/link.txt").containsNoBytes();
byte[] bytes = {0, 1, 2, 3};
Files.write(path("/file.txt"), bytes);
assertThatPath("/file.txt").containsBytes(bytes);
assertThatPath("/link.txt").containsBytes(bytes);
Files.write(path("/link.txt"), bytes, APPEND);
assertThatPath("/file.txt").containsBytes(0, 1, 2, 3, 0, 1, 2, 3);
assertThatPath("/link.txt").containsBytes(0, 1, 2, 3, 0, 1, 2, 3);
Files.delete(path("/file.txt"));
assertThatPath("/link.txt").hasLinkCount(1);
assertThatPath("/link.txt").containsBytes(0, 1, 2, 3, 0, 1, 2, 3);
}
@Test
public void testLink_forSymbolicLink_usesSymbolicLinkTarget() throws IOException {
Files.createFile(path("/file"));
Files.createSymbolicLink(path("/symlink"), path("/file"));
Object key = getFileKey("/file");
Files.createLink(path("/link"), path("/symlink"));
assertThatPath("/link").isRegularFile()
.and().hasLinkCount(2)
.and().attribute("fileKey").is(key);
}
@Test
public void testLink_failsWhenTargetDoesNotExist() throws IOException {
try {
Files.createLink(path("/link"), path("/foo"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/foo", expected.getFile());
}
Files.createSymbolicLink(path("/foo"), path("/bar"));
try {
Files.createLink(path("/link"), path("/foo"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/foo", expected.getFile());
}
}
@Test
public void testLink_failsForNonRegularFile() throws IOException {
Files.createDirectory(path("/dir"));
try {
Files.createLink(path("/link"), path("/dir"));
fail();
} catch (FileSystemException expected) {
assertEquals("/link", expected.getFile());
assertEquals("/dir", expected.getOtherFile());
}
assertThatPath("/link").doesNotExist();
}
@Test
public void testLinks_failsWhenTargetFileAlreadyExists() throws IOException {
Files.createFile(path("/file"));
Files.createFile(path("/link"));
try {
Files.createLink(path("/link"), path("/file"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/link", expected.getFile());
}
}
@Test
public void testStreams() throws IOException {
try (OutputStream out = Files.newOutputStream(path("/test"))) {
for (int i = 0; i < 100; i++) {
out.write(i);
}
}
byte[] expected = new byte[100];
for (byte i = 0; i < 100; i++) {
expected[i] = i;
}
try (InputStream in = Files.newInputStream(path("/test"))) {
byte[] bytes = new byte[100];
ByteStreams.readFully(in, bytes);
assertArrayEquals(expected, bytes);
}
try (Writer writer = Files.newBufferedWriter(path("/test.txt"), UTF_8)) {
writer.write("hello");
}
try (Reader reader = Files.newBufferedReader(path("/test.txt"), UTF_8)) {
assertEquals("hello", CharStreams.toString(reader));
}
try (Writer writer = Files.newBufferedWriter(path("/test.txt"), UTF_8, APPEND)) {
writer.write(" world");
}
try (Reader reader = Files.newBufferedReader(path("/test.txt"), UTF_8)) {
assertEquals("hello world", CharStreams.toString(reader));
}
}
@Test
public void testChannels() throws IOException {
try (FileChannel channel = FileChannel.open(path("/test.txt"), CREATE_NEW, WRITE)) {
ByteBuffer buf1 = UTF_8.encode("hello");
ByteBuffer buf2 = UTF_8.encode(" world");
while (buf1.hasRemaining() || buf2.hasRemaining()) {
channel.write(new ByteBuffer[] {buf1, buf2});
}
assertEquals(11, channel.position());
assertEquals(11, channel.size());
channel.write(UTF_8.encode("!"));
assertEquals(12, channel.position());
assertEquals(12, channel.size());
}
try (SeekableByteChannel channel = Files.newByteChannel(path("/test.txt"), READ)) {
assertEquals(0, channel.position());
assertEquals(12, channel.size());
ByteBuffer buffer = ByteBuffer.allocate(100);
while (channel.read(buffer) != -1) {}
buffer.flip();
assertEquals("hello world!", UTF_8.decode(buffer).toString());
}
byte[] bytes = preFilledBytes(100);
Files.write(path("/test"), bytes);
try (SeekableByteChannel channel = Files.newByteChannel(path("/test"), READ, WRITE)) {
ByteBuffer buffer = ByteBuffer.wrap(preFilledBytes(50));
channel.position(50);
channel.write(buffer);
buffer.flip();
channel.write(buffer);
channel.position(0);
ByteBuffer readBuffer = ByteBuffer.allocate(150);
while (readBuffer.hasRemaining()) {
channel.read(readBuffer);
}
byte[] expected = Bytes.concat(preFilledBytes(50), preFilledBytes(50), preFilledBytes(50));
assertArrayEquals(expected, readBuffer.array());
}
try (FileChannel channel = FileChannel.open(path("/test"), READ, WRITE)) {
assertEquals(150, channel.size());
channel.truncate(10);
assertEquals(10, channel.size());
ByteBuffer buffer = ByteBuffer.allocate(20);
assertEquals(10, channel.read(buffer));
buffer.flip();
byte[] expected = new byte[20];
System.arraycopy(preFilledBytes(10), 0, expected, 0, 10);
assertArrayEquals(expected, buffer.array());
}
}
@Test
public void testCopy_inputStreamToFile() throws IOException {
byte[] bytes = preFilledBytes(512);
Files.copy(new ByteArrayInputStream(bytes), path("/test"));
assertThatPath("/test").containsBytes(bytes);
try {
Files.copy(new ByteArrayInputStream(bytes), path("/test"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/test", expected.getMessage());
}
Files.copy(new ByteArrayInputStream(bytes), path("/test"), REPLACE_EXISTING);
assertThatPath("/test").containsBytes(bytes);
Files.copy(new ByteArrayInputStream(bytes), path("/foo"), REPLACE_EXISTING);
assertThatPath("/foo").containsBytes(bytes);
}
@Test
public void testCopy_fileToOutputStream() throws IOException {
byte[] bytes = preFilledBytes(512);
Files.write(path("/test"), bytes);
ByteArrayOutputStream out = new ByteArrayOutputStream();
Files.copy(path("/test"), out);
assertArrayEquals(bytes, out.toByteArray());
}
@Test
public void testCopy_fileToPath() throws IOException {
byte[] bytes = preFilledBytes(512);
Files.write(path("/foo"), bytes);
assertThatPath("/bar").doesNotExist();
Files.copy(path("/foo"), path("/bar"));
assertThatPath("/bar").containsBytes(bytes);
byte[] moreBytes = preFilledBytes(2048);
Files.write(path("/baz"), moreBytes);
Files.copy(path("/baz"), path("/bar"), REPLACE_EXISTING);
assertThatPath("/bar").containsBytes(moreBytes);
try {
Files.copy(path("/none"), path("/bar"));
fail();
} catch (NoSuchFileException expected) {
assertEquals("/none", expected.getMessage());
}
}
@Test
public void testCopy_withCopyAttributes() throws IOException {
Path foo = path("/foo");
Files.createFile(foo);
Files.getFileAttributeView(foo, BasicFileAttributeView.class)
.setTimes(FileTime.fromMillis(100), FileTime.fromMillis(1000), FileTime.fromMillis(10000));
assertThat(Files.getAttribute(foo, "lastModifiedTime")).isEqualTo(FileTime.fromMillis(100));
UserPrincipal zero = fs.getUserPrincipalLookupService().lookupPrincipalByName("zero");
Files.setAttribute(foo, "owner:owner", zero);
Path bar = path("/bar");
Files.copy(foo, bar, COPY_ATTRIBUTES);
BasicFileAttributes attributes = Files.readAttributes(bar, BasicFileAttributes.class);
assertThat(attributes.lastModifiedTime()).isEqualTo(FileTime.fromMillis(100));
assertThat(attributes.lastAccessTime()).isEqualTo(FileTime.fromMillis(1000));
assertThat(attributes.creationTime()).isEqualTo(FileTime.fromMillis(10000));
assertThat(Files.getAttribute(bar, "owner:owner")).isEqualTo(zero);
Path baz = path("/baz");
Files.copy(foo, baz);
// test that attributes are not copied when COPY_ATTRIBUTES is not specified
attributes = Files.readAttributes(baz, BasicFileAttributes.class);
assertThat(attributes.lastModifiedTime()).isNotEqualTo(FileTime.fromMillis(100));
assertThat(attributes.lastAccessTime()).isNotEqualTo(FileTime.fromMillis(1000));
assertThat(attributes.creationTime()).isNotEqualTo(FileTime.fromMillis(10000));
assertThat(Files.getAttribute(baz, "owner:owner")).isNotEqualTo(zero);
}
@Test
public void testCopy_doesNotSupportAtomicMove() throws IOException {
try {
Files.copy(path("/foo"), path("/bar"), ATOMIC_MOVE);
fail();
} catch (UnsupportedOperationException expected) {
}
}
@Test
public void testCopy_directoryToPath() throws IOException {
Files.createDirectory(path("/foo"));
assertThatPath("/bar").doesNotExist();
Files.copy(path("/foo"), path("/bar"));
assertThatPath("/bar").isDirectory();
}
@Test
public void testCopy_withoutReplaceExisting_failsWhenTargetExists() throws IOException {
Files.createFile(path("/bar"));
Files.createDirectory(path("/foo"));
// dir -> file
try {
Files.copy(path("/foo"), path("/bar"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/bar", expected.getMessage());
}
Files.delete(path("/foo"));
Files.createFile(path("/foo"));
// file -> file
try {
Files.copy(path("/foo"), path("/bar"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/bar", expected.getMessage());
}
Files.delete(path("/bar"));
Files.createDirectory(path("/bar"));
// file -> dir
try {
Files.copy(path("/foo"), path("/bar"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/bar", expected.getMessage());
}
Files.delete(path("/foo"));
Files.createDirectory(path("/foo"));
// dir -> dir
try {
Files.copy(path("/foo"), path("/bar"));
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/bar", expected.getMessage());
}
}
@Test
public void testCopy_withReplaceExisting() throws IOException {
Files.createFile(path("/bar"));
Files.createDirectory(path("/test"));
assertThatPath("/bar").isRegularFile();
// overwrite regular file w/ directory
Files.copy(path("/test"), path("/bar"), REPLACE_EXISTING);
assertThatPath("/bar").isDirectory();
byte[] bytes = {0, 1, 2, 3};
Files.write(path("/baz"), bytes);
// overwrite directory w/ regular file
Files.copy(path("/baz"), path("/bar"), REPLACE_EXISTING);
assertThatPath("/bar").containsSameBytesAs("/baz");
}
@Test
public void testCopy_withReplaceExisting_cantReplaceNonEmptyDirectory() throws IOException {
Files.createDirectory(path("/foo"));
Files.createDirectory(path("/foo/bar"));
Files.createFile(path("/foo/baz"));
Files.createDirectory(path("/test"));
try {
Files.copy(path("/test"), path("/foo"), REPLACE_EXISTING);
fail();
} catch (DirectoryNotEmptyException expected) {
assertEquals("/foo", expected.getMessage());
}
Files.delete(path("/test"));
Files.createFile(path("/test"));
try {
Files.copy(path("/test"), path("/foo"), REPLACE_EXISTING);
fail();
} catch (DirectoryNotEmptyException expected) {
assertEquals("/foo", expected.getMessage());
}
Files.delete(path("/foo/baz"));
Files.delete(path("/foo/bar"));
Files.copy(path("/test"), path("/foo"), REPLACE_EXISTING);
assertThatPath("/foo").isRegularFile(); // replaced
}
@Test
public void testCopy_directoryToPath_doesNotCopyDirectoryContents() throws IOException {
Files.createDirectory(path("/foo"));
Files.createDirectory(path("/foo/baz"));
Files.createFile(path("/foo/test"));
Files.copy(path("/foo"), path("/bar"));
assertThatPath("/bar").hasNoChildren();
}
@Test
public void testCopy_symbolicLinkToPath() throws IOException {
byte[] bytes = preFilledBytes(128);
Files.write(path("/test"), bytes);
Files.createSymbolicLink(path("/link"), path("/test"));
assertThatPath("/bar").doesNotExist();
Files.copy(path("/link"), path("/bar"));
assertThatPath("/bar", NOFOLLOW_LINKS).containsBytes(bytes);
Files.delete(path("/bar"));
Files.copy(path("/link"), path("/bar"), NOFOLLOW_LINKS);
assertThatPath("/bar", NOFOLLOW_LINKS).isSymbolicLink().withTarget("/test");
assertThatPath("/bar").isRegularFile();
assertThatPath("/bar").containsBytes(bytes);
Files.delete(path("/test"));
assertThatPath("/bar", NOFOLLOW_LINKS).isSymbolicLink();
assertThatPath("/bar").doesNotExist();
}
@Test
public void testCopy_toDifferentFileSystem() throws IOException {
try (FileSystem fs2 = Jimfs.newFileSystem(UNIX_CONFIGURATION)) {
Path foo = fs.getPath("/foo");
byte[] bytes = {0, 1, 2, 3, 4};
Files.write(foo, bytes);
Path foo2 = fs2.getPath("/foo");
Files.copy(foo, foo2);
assertThatPath(foo).exists();
assertThatPath(foo2).exists()
.and().containsBytes(bytes);
}
}
@Test
public void testCopy_toDifferentFileSystem_copyAttributes() throws IOException {
try (FileSystem fs2 = Jimfs.newFileSystem(UNIX_CONFIGURATION)) {
Path foo = fs.getPath("/foo");
byte[] bytes = {0, 1, 2, 3, 4};
Files.write(foo, bytes);
Files.getFileAttributeView(foo, BasicFileAttributeView.class)
.setTimes(FileTime.fromMillis(0), FileTime.fromMillis(1), FileTime.fromMillis(2));
UserPrincipal owner = fs.getUserPrincipalLookupService().lookupPrincipalByName("foobar");
Files.setOwner(foo, owner);
assertThatPath(foo).attribute("owner:owner").is(owner);
Path foo2 = fs2.getPath("/foo");
Files.copy(foo, foo2, COPY_ATTRIBUTES);
assertThatPath(foo).exists();
// when copying with COPY_ATTRIBUTES to a different FileSystem, only basic attributes (that
// is, file times) can actually be copied
assertThatPath(foo2).exists()
.and().attribute("lastModifiedTime").is(FileTime.fromMillis(0))
.and().attribute("lastAccessTime").is(FileTime.fromMillis(1))
.and().attribute("creationTime").is(FileTime.fromMillis(2))
.and().attribute("owner:owner").isNot(owner)
.and().attribute("owner:owner")
.isNot(fs2.getUserPrincipalLookupService().lookupPrincipalByName("foobar"))
.and().containsBytes(bytes); // do this last; it updates the access time
}
}
@Test
public void testMove() throws IOException {
byte[] bytes = preFilledBytes(100);
Files.write(path("/foo"), bytes);
Object fooKey = getFileKey("/foo");
Files.move(path("/foo"), path("/bar"));
assertThatPath("/foo").doesNotExist()
.andThat("/bar").containsBytes(bytes)
.and().attribute("fileKey").is(fooKey);
Files.createDirectory(path("/foo"));
Files.move(path("/bar"), path("/foo/bar"));
assertThatPath("/bar").doesNotExist()
.andThat("/foo/bar").isRegularFile();
Files.move(path("/foo"), path("/baz"));
assertThatPath("/foo").doesNotExist()
.andThat("/baz").isDirectory()
.andThat("/baz/bar").isRegularFile();
}
@Test
public void testMove_movesSymbolicLinkNotTarget() throws IOException {
byte[] bytes = preFilledBytes(100);
Files.write(path("/foo.txt"), bytes);
Files.createSymbolicLink(path("/link"), path("foo.txt"));
Files.move(path("/link"), path("/link.txt"));
assertThatPath("/foo.txt").noFollowLinks().isRegularFile()
.and().containsBytes(bytes);
assertThatPath(path("/link")).doesNotExist();
assertThatPath(path("/link.txt")).noFollowLinks().isSymbolicLink();
assertThatPath(path("/link.txt")).isRegularFile()
.and().containsBytes(bytes);
}
@Test
public void testMove_cannotMoveDirIntoOwnSubtree() throws IOException {
Files.createDirectories(path("/foo"));
try {
Files.move(path("/foo"), path("/foo/bar"));
fail();
} catch (IOException expected) {
assertThat(expected.getMessage()).contains("sub");
}
Files.createDirectories(path("/foo/bar/baz/stuff"));
Files.createDirectories(path("/hello/world"));
Files.createSymbolicLink(path("/hello/world/link"), path("../../foo/bar/baz"));
try {
Files.move(path("/foo/bar"), path("/hello/world/link/bar"));
fail();
} catch (IOException expected) {
assertThat(expected.getMessage()).contains("sub");
}
}
@Test
public void testMove_withoutReplaceExisting_failsWhenTargetExists() throws IOException {
byte[] bytes = preFilledBytes(50);
Files.write(path("/test"), bytes);
Object testKey = getFileKey("/test");
Files.createFile(path("/bar"));
try {
Files.move(path("/test"), path("/bar"), ATOMIC_MOVE);
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/bar", expected.getMessage());
}
assertThatPath("/test").containsBytes(bytes)
.and().attribute("fileKey").is(testKey);
Files.delete(path("/bar"));
Files.createDirectory(path("/bar"));
try {
Files.move(path("/test"), path("/bar"), ATOMIC_MOVE);
fail();
} catch (FileAlreadyExistsException expected) {
assertEquals("/bar", expected.getMessage());
}
assertThatPath("/test").containsBytes(bytes)
.and().attribute("fileKey").is(testKey);
}
@Test
public void testMove_toDifferentFileSystem() throws IOException {
try (FileSystem fs2 = Jimfs.newFileSystem(Configuration.unix())) {
Path foo = fs.getPath("/foo");
byte[] bytes = {0, 1, 2, 3, 4};
Files.write(foo, bytes);
Files.getFileAttributeView(foo, BasicFileAttributeView.class)
.setTimes(FileTime.fromMillis(0), FileTime.fromMillis(1), FileTime.fromMillis(2));
Path foo2 = fs2.getPath("/foo");
Files.move(foo, foo2);
assertThatPath(foo).doesNotExist();
assertThatPath(foo2).exists()
.and().attribute("lastModifiedTime").is(FileTime.fromMillis(0))
.and().attribute("lastAccessTime").is(FileTime.fromMillis(1))
.and().attribute("creationTime").is(FileTime.fromMillis(2))
.and().containsBytes(bytes); // do this last; it updates the access time
}
}
@Test
public void testIsSameFile() throws IOException {
Files.createDirectory(path("/foo"));
Files.createSymbolicLink(path("/bar"), path("/foo"));
Files.createFile(path("/bar/test"));
assertThatPath("/foo").isSameFileAs("/foo");
assertThatPath("/bar").isSameFileAs("/bar");
assertThatPath("/foo/test").isSameFileAs("/foo/test");
assertThatPath("/bar/test").isSameFileAs("/bar/test");
assertThatPath("/foo").isNotSameFileAs("test");
assertThatPath("/bar").isNotSameFileAs("/test");
assertThatPath("/foo").isSameFileAs("/bar");
assertThatPath("/foo/test").isSameFileAs("/bar/test");
Files.createSymbolicLink(path("/baz"), path("bar")); // relative path
assertThatPath("/baz").isSameFileAs("/foo");
assertThatPath("/baz/test").isSameFileAs("/foo/test");
}
@Test
public void testIsSameFile_forPathFromDifferentFileSystemProvider() throws IOException {
Path defaultFileSystemRoot =
FileSystems.getDefault()
.getRootDirectories()
.iterator()
.next();
assertThat(Files.isSameFile(path("/"), defaultFileSystemRoot)).isFalse();
}
@Test
public void testPathLookups() throws IOException {
assertThatPath("/").isSameFileAs("/");
assertThatPath("/..").isSameFileAs("/");
assertThatPath("/../../..").isSameFileAs("/");
assertThatPath("../../../..").isSameFileAs("/");
assertThatPath("").isSameFileAs("/work");
Files.createDirectories(path("/foo/bar/baz"));
Files.createSymbolicLink(path("/foo/bar/link1"), path("../link2"));
Files.createSymbolicLink(path("/foo/link2"), path("/"));
assertThatPath("/foo/bar/link1/foo/bar/link1/foo").isSameFileAs("/foo");
}
@Test
public void testSecureDirectoryStream() throws IOException {
Files.createDirectories(path("/foo/bar"));
Files.createFile(path("/foo/a"));
Files.createFile(path("/foo/b"));
Files.createSymbolicLink(path("/foo/barLink"), path("bar"));
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path("/foo"))) {
if (!(stream instanceof SecureDirectoryStream)) {
fail("should be a secure directory stream");
}
SecureDirectoryStream<Path> secureStream = (SecureDirectoryStream<Path>) stream;
assertThat(ImmutableList.copyOf(secureStream))
.isEqualTo(
ImmutableList.of(
path("/foo/a"), path("/foo/b"), path("/foo/bar"), path("/foo/barLink")));
secureStream.deleteFile(path("b"));
assertThatPath("/foo/b").doesNotExist();
secureStream.newByteChannel(path("b"), ImmutableSet.of(WRITE, CREATE_NEW)).close();
assertThatPath("/foo/b").isRegularFile();
assertThatPath("/foo").hasChildren("a", "b", "bar", "barLink");
Files.createDirectory(path("/baz"));
Files.move(path("/foo"), path("/baz/stuff"));
assertThatPath(path("/foo")).doesNotExist();
assertThatPath("/baz/stuff").hasChildren("a", "b", "bar", "barLink");
secureStream.deleteFile(path("b"));
assertThatPath("/baz/stuff/b").doesNotExist();
assertThatPath("/baz/stuff").hasChildren("a", "bar", "barLink");
assertThat(
secureStream
.getFileAttributeView(BasicFileAttributeView.class)
.readAttributes()
.isDirectory())
.isTrue();
assertThat(
secureStream
.getFileAttributeView(path("a"), BasicFileAttributeView.class)
.readAttributes()
.isRegularFile())
.isTrue();
try {
secureStream.deleteFile(path("bar"));
fail();
} catch (FileSystemException expected) {
assertThat(expected.getFile()).isEqualTo("bar");
}
try {
secureStream.deleteDirectory(path("a"));
fail();
} catch (FileSystemException expected) {
assertThat(expected.getFile()).isEqualTo("a");
}
try (SecureDirectoryStream<Path> barStream = secureStream.newDirectoryStream(path("bar"))) {
barStream.newByteChannel(path("stuff"), ImmutableSet.of(WRITE, CREATE_NEW)).close();
assertThat(
barStream
.getFileAttributeView(path("stuff"), BasicFileAttributeView.class)
.readAttributes()
.isRegularFile())
.isTrue();
assertThat(
secureStream
.getFileAttributeView(path("bar/stuff"), BasicFileAttributeView.class)
.readAttributes()
.isRegularFile())
.isTrue();
}
try (SecureDirectoryStream<Path> barLinkStream =
secureStream.newDirectoryStream(path("barLink"))) {
assertThat(
barLinkStream
.getFileAttributeView(path("stuff"), BasicFileAttributeView.class)
.readAttributes()
.isRegularFile())
.isTrue();
assertThat(
barLinkStream
.getFileAttributeView(path(".."), BasicFileAttributeView.class)
.readAttributes()
.isDirectory())
.isTrue();
}
try {
secureStream.newDirectoryStream(path("barLink"), NOFOLLOW_LINKS);
fail();
} catch (NotDirectoryException expected) {
assertThat(expected.getFile()).isEqualTo("barLink");
}
try (SecureDirectoryStream<Path> barStream = secureStream.newDirectoryStream(path("bar"))) {
secureStream.move(path("a"), barStream, path("moved"));
assertThatPath(path("/baz/stuff/a")).doesNotExist();
assertThatPath(path("/baz/stuff/bar/moved")).isRegularFile();
assertThat(
barStream
.getFileAttributeView(path("moved"), BasicFileAttributeView.class)
.readAttributes()
.isRegularFile())
.isTrue();
}
}
}
@Test
public void testSecureDirectoryStreamBasedOnRelativePath() throws IOException {
Files.createDirectories(path("foo"));
Files.createFile(path("foo/a"));
Files.createFile(path("foo/b"));
Files.createDirectory(path("foo/c"));
Files.createFile(path("foo/c/d"));
Files.createFile(path("foo/c/e"));
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path("foo"))) {
SecureDirectoryStream<Path> secureStream = (SecureDirectoryStream<Path>) stream;
assertThat(ImmutableList.copyOf(secureStream))
.containsExactly(path("foo/a"), path("foo/b"), path("foo/c"));
try (DirectoryStream<Path> stream2 = secureStream.newDirectoryStream(path("c"))) {
assertThat(ImmutableList.copyOf(stream2)).containsExactly(path("foo/c/d"), path("foo/c/e"));
}
}
}
@Test
public void testClosedSecureDirectoryStream() throws IOException {
Files.createDirectory(path("/foo"));
SecureDirectoryStream<Path> stream =
(SecureDirectoryStream<Path>) Files.newDirectoryStream(path("/foo"));
stream.close();
try {
stream.iterator();
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
stream.deleteDirectory(fs.getPath("a"));
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
stream.deleteFile(fs.getPath("a"));
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
stream.newByteChannel(fs.getPath("a"), ImmutableSet.of(CREATE, WRITE));
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
stream.newDirectoryStream(fs.getPath("a"));
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
stream.move(fs.getPath("a"), stream, fs.getPath("b"));
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
stream.getFileAttributeView(BasicFileAttributeView.class);
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
stream.getFileAttributeView(fs.getPath("a"), BasicFileAttributeView.class);
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
}
@Test
public void testClosedSecureDirectoryStreamAttributeViewAndIterator() throws IOException {
Files.createDirectory(path("/foo"));
Files.createDirectory(path("/foo/bar"));
SecureDirectoryStream<Path> stream =
(SecureDirectoryStream<Path>) Files.newDirectoryStream(path("/foo"));
Iterator<Path> iter = stream.iterator();
BasicFileAttributeView view1 = stream.getFileAttributeView(BasicFileAttributeView.class);
BasicFileAttributeView view2 =
stream.getFileAttributeView(path("bar"), BasicFileAttributeView.class);
try {
stream.iterator();
fail("expected IllegalStateException");
} catch (IllegalStateException expected) {
}
stream.close();
try {
iter.next();
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
view1.readAttributes();
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
view2.readAttributes();
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
view1.setTimes(null, null, null);
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
try {
view2.setTimes(null, null, null);
fail("expected ClosedDirectoryStreamException");
} catch (ClosedDirectoryStreamException expected) {
}
}
@Test
public void testDirectoryAccessAndModifiedTimeUpdates() throws IOException {
Files.createDirectories(path("/foo/bar"));
FileTimeTester tester = new FileTimeTester(path("/foo/bar"));
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeDidNotChange();
// TODO(cgdecker): Use a Clock for file times so I can test this reliably without sleeping
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
Files.createFile(path("/foo/bar/baz.txt"));
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeChanged();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
// access time is updated by reading the full contents of the directory
// not just by doing a lookup in it
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path("/foo/bar"))) {
// iterate the stream, forcing the directory to actually be read
Iterators.advance(stream.iterator(), Integer.MAX_VALUE);
}
tester.assertAccessTimeChanged();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
Files.move(path("/foo/bar/baz.txt"), path("/foo/bar/baz2.txt"));
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeChanged();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
Files.delete(path("/foo/bar/baz2.txt"));
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeChanged();
}
@Test
public void testRegularFileAccessAndModifiedTimeUpdates() throws IOException {
Path foo = path("foo");
Files.createFile(foo);
FileTimeTester tester = new FileTimeTester(foo);
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
try (FileChannel channel = FileChannel.open(foo, READ)) {
// opening READ channel does not change times
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
channel.read(ByteBuffer.allocate(100));
// read call on channel does
tester.assertAccessTimeChanged();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
channel.read(ByteBuffer.allocate(100));
tester.assertAccessTimeChanged();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
try {
channel.write(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}));
} catch (NonWritableChannelException ignore) {
}
// failed write on non-readable channel does not change times
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
}
// closing channel does not change times
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
try (FileChannel channel = FileChannel.open(foo, WRITE)) {
// opening WRITE channel does not change times
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
channel.write(ByteBuffer.wrap(new byte[] {0, 1, 2, 3}));
// write call on channel does
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeChanged();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
channel.write(ByteBuffer.wrap(new byte[] {4, 5, 6, 7}));
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeChanged();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
try {
channel.read(ByteBuffer.allocate(100));
} catch (NonReadableChannelException ignore) {
}
// failed read on non-readable channel does not change times
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeDidNotChange();
Uninterruptibles.sleepUninterruptibly(1, MILLISECONDS);
}
// closing channel does not change times
tester.assertAccessTimeDidNotChange();
tester.assertModifiedTimeDidNotChange();
}
@Test
public void testUnsupportedFeatures() throws IOException {
FileSystem fileSystem =
Jimfs.newFileSystem(
Configuration.unix()
.toBuilder()
.setSupportedFeatures() // none
.build());
Path foo = fileSystem.getPath("foo");
Path bar = foo.resolveSibling("bar");
try {
Files.createLink(foo, bar);
fail();
} catch (UnsupportedOperationException expected) {
}
try {
Files.createSymbolicLink(foo, bar);
fail();
} catch (UnsupportedOperationException expected) {
}
try {
Files.readSymbolicLink(foo);
fail();
} catch (UnsupportedOperationException expected) {
}
try {
FileChannel.open(foo);
fail();
} catch (UnsupportedOperationException expected) {
}
try {
AsynchronousFileChannel.open(foo);
fail();
} catch (UnsupportedOperationException expected) {
}
Files.createDirectory(foo);
Files.createFile(bar);
try (DirectoryStream<Path> stream = Files.newDirectoryStream(foo)) {
assertThat(stream).isNotInstanceOf(SecureDirectoryStream.class);
}
try (SeekableByteChannel channel = Files.newByteChannel(bar)) {
assertThat(channel).isNotInstanceOf(FileChannel.class);
}
}
}