package com.github.marschall.memoryfilesystem;
import static com.github.marschall.memoryfilesystem.FileContentsMatcher.hasContents;
import static com.github.marschall.memoryfilesystem.FileExistsMatcher.exists;
import static com.github.marschall.memoryfilesystem.FileUtility.setContents;
import static com.github.marschall.memoryfilesystem.IsSameFileMatcher.isSameFile;
import static com.github.marschall.memoryfilesystem.IsSymbolicLinkMatcher.isSymbolicLink;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.WRITE;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
public class MemoryFileSystemCopyTest {
@Rule
public final FileSystemRule rule = new FileSystemRule();
@Test
public void copySymbolicLinkNoFollow() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
// /link -> /file
// copy /link to /copy with follow no symlinks
Path file = fileSystem.getPath("/").resolve("file");
Files.createFile(file);
Path link = file.resolveSibling("link");
Path copy = file.resolveSibling("copy");
Files.createSymbolicLink(link, file);
Files.copy(link, copy, NOFOLLOW_LINKS);
assertThat(copy, exists());
assertThat(copy, isSymbolicLink());
assertEquals("/file", copy.toRealPath().toString());
}
@Test
public void copySymbolicLinkFollow() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
// /link -> /file
// copy /link to /copy with follow symlinks
Path file = fileSystem.getPath("/").resolve("file");
Path copy = fileSystem.getPath("/").resolve("copy");
Files.createFile(file);
Path link = file.resolveSibling("link");
Files.createSymbolicLink(link, file);
Files.copy(link, copy);
assertThat(copy, exists());
assertThat(copy, not(isSymbolicLink()));
}
@Test
public void copySymbolicLinkReplace() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
// /target -> /file1
// copy /file2 to /target with replace existing
Path file1 = fileSystem.getPath("/").resolve("file1");
Path file2 = fileSystem.getPath("/").resolve("file2");
Files.createFile(file1);
Files.createFile(file2);
Path target = file1.resolveSibling("target");
Files.createSymbolicLink(target, file1);
Files.copy(file2, target, REPLACE_EXISTING);
assertThat(target, exists());
assertThat(target, not(isSymbolicLink()));
assertEquals("/target", target.toRealPath().toString());
}
@Test
public void copySymbolicLinkReplaceNoFollow() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
// /link -> /file1
// /target -> /file1
// copy /link to /target with replace existing and no follow
Path link = fileSystem.getPath("/").resolve("link");
Path file1 = link.resolveSibling("file1");
Files.createFile(file1);
Files.createSymbolicLink(link, file1);
Path target = fileSystem.getPath("/").resolve("target");
Files.createSymbolicLink(target, link.resolveSibling("file2"));
Files.copy(link, target, REPLACE_EXISTING, NOFOLLOW_LINKS);
assertThat(target, exists(NOFOLLOW_LINKS));
assertThat(target, isSymbolicLink());
assertEquals("/file1", target.toRealPath().toString());
}
@Test
public void copySameFile() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path a = fileSystem.getPath("a");
Path b = fileSystem.getPath("./b/../a");
this.createAndSetContents(a, "aaa");
assertThat(a, exists());
assertThat(b, exists());
assertThat(a, isSameFile(b));
Files.copy(a, b);
assertThat(a, exists());
assertThat(b, exists());
assertThat(a, hasContents("aaa"));
assertThat(b, hasContents("aaa"));
}
@Test
public void copyRootIntoItself() throws IOException {
Path root = this.rule.getFileSystem().getPath("/");
Files.copy(root, root);
}
@Test
public void copyRootDifferentFileSystems() throws IOException {
Path firstRoot = this.rule.getFileSystem().getPath("/");
try (FileSystem second = MemoryFileSystemBuilder.newEmpty().build("second");) {
Path secondRoot = second.getPath("/");
try {
Files.copy(firstRoot, secondRoot);
fail("moving the root should not work");
} catch (IOException e) {
// expected
}
try {
Files.copy(secondRoot, firstRoot);
fail("moving the root should not work");
} catch (IOException e) {
// expected
}
}
}
@Test
public void copyRoot() throws IOException {
Path root = this.rule.getFileSystem().getPath("/");
Path path = this.rule.getFileSystem().getPath("/a");
try {
Files.copy(root, path);
fail("moving the root should not work");
} catch (IOException e) {
// expected
}
try {
Files.copy(path, root);
fail("moving the root should not work");
} catch (IOException e) {
// expected
}
}
@Test
public void copyAlreadyExists() throws IOException, ParseException {
// copying a folder to an already existing one should throw FileAlreadyExistsException
FileSystem fileSystem = this.rule.getFileSystem();
Path source = fileSystem.getPath("source");
Path target = fileSystem.getPath("target");
Files.createDirectory(source);
Files.createDirectory(target);
try {
Files.copy(source, target);
fail("should not be able to overwrite exsiting directories");
} catch (FileAlreadyExistsException e) {
// should reach here
assert(true);
}
}
@Test
public void copyAlreadyExistsNotEmpty() throws IOException, ParseException {
// copying a folder to an already existing one that is not empty should throw DirectoryNotEmptyException
FileSystem fileSystem = this.rule.getFileSystem();
Path source = fileSystem.getPath("source");
Path target = fileSystem.getPath("target");
Path child = fileSystem.getPath("target/child.txt");
Files.createDirectory(source);
Files.createDirectory(target);
Files.createFile(child);
try {
Files.copy(source, target, REPLACE_EXISTING);
fail("should not be able to overwrite non-empty directories");
} catch (DirectoryNotEmptyException e) {
// should reach here
assert(true);
}
}
@Test
public void copyOverwriteExists() throws IOException, ParseException {
// copying a folder to an already existing one should work with REPLACE_EXISTING
FileSystem fileSystem = this.rule.getFileSystem();
Path source = fileSystem.getPath("source");
Path target = fileSystem.getPath("target");
Files.createDirectory(source);
Files.createDirectory(target);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
FileTime sourceTime = FileTime.fromMillis(format.parse("2012-11-07T20:30:22").getTime());
FileTime targetTime = FileTime.fromMillis(format.parse("2012-10-07T20:30:22").getTime());
Files.setLastModifiedTime(source, sourceTime);
Files.setLastModifiedTime(target, targetTime);
assertEquals(sourceTime, Files.getLastModifiedTime(source));
assertEquals(targetTime, Files.getLastModifiedTime(target));
Files.copy(source, target, REPLACE_EXISTING, COPY_ATTRIBUTES);
assertThat(source, exists());
assertThat(target, exists());
assertEquals(sourceTime, Files.getLastModifiedTime(source));
assertEquals(sourceTime, Files.getLastModifiedTime(target));
}
@Test
public void copyAttributes() throws IOException, ParseException {
FileSystem fileSystem = this.rule.getFileSystem();
Path source = fileSystem.getPath("/source.txt");
Path target = fileSystem.getPath("/target.txt");
Files.createFile(source);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
FileTime lastModifiedTime = FileTime.fromMillis(format.parse("2012-11-07T20:30:22").getTime());
FileTime lastAccessedTime = FileTime.fromMillis(format.parse("2012-10-07T20:30:22").getTime());
FileTime createTime = FileTime.fromMillis(format.parse("2012-09-07T20:30:22").getTime());
BasicFileAttributeView sourceBasicFileAttributeView = Files.getFileAttributeView(source, BasicFileAttributeView.class);
BasicFileAttributes sourceBasicAttributes = sourceBasicFileAttributeView.readAttributes();
assertNotEquals(lastModifiedTime, sourceBasicAttributes.lastModifiedTime());
assertNotEquals(lastAccessedTime, sourceBasicAttributes.lastAccessTime());
assertNotEquals(createTime, sourceBasicAttributes.creationTime());
sourceBasicFileAttributeView.setTimes(lastModifiedTime, lastAccessedTime, createTime);
Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES);
BasicFileAttributeView targetBasicFileAttributeView = Files.getFileAttributeView(target, BasicFileAttributeView.class);
BasicFileAttributes targetBasicAttributes = targetBasicFileAttributeView.readAttributes();
assertEquals(lastModifiedTime, targetBasicAttributes.lastModifiedTime());
assertEquals(lastAccessedTime, targetBasicAttributes.lastAccessTime());
assertEquals(createTime, targetBasicAttributes.creationTime());
}
@Test
public void copyNoExisitingNoAttributes() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path a = fileSystem.getPath("/1/a");
Path b = fileSystem.getPath("/2/b");
Files.createDirectories(b.toAbsolutePath().getParent());
this.createAndSetContents(a, "aaa");
assertThat(a, exists());
assertThat(b, not(exists()));
Files.copy(a, b);
assertThat(a, exists());
assertThat(b, exists());
assertThat(a, hasContents("aaa"));
assertThat(b, hasContents("aaa"));
setContents(a, "a1");
assertThat(a, hasContents("a1"));
assertThat(b, hasContents("aaa"));
}
@Test
public void copyAcrossFileSystems() throws IOException {
FileSystem source = this.rule.getFileSystem();
try (FileSystem target = MemoryFileSystemBuilder.newEmpty().build("target")) {
Path a = source.getPath("a");
Path b = target.getPath("b");
this.createAndSetContents(a, "aaa");
assertThat(a, exists());
assertThat(b, not(exists()));
Files.copy(a, b);
assertThat(a, exists());
assertThat(b, exists());
assertThat(a, hasContents("aaa"));
assertThat(b, hasContents("aaa"));
setContents(a, "a1");
assertThat(a, hasContents("a1"));
assertThat(b, hasContents("aaa"));
}
}
@Test
public void copyReplaceExisitingNoAttributes() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path a = fileSystem.getPath("/1/a");
Path b = fileSystem.getPath("/2/b");
this.createAndSetContents(a, "aaa");
this.createAndSetContents(b, "bbb");
assertThat(a, exists());
assertThat(b, exists());
Files.copy(a, b, StandardCopyOption.REPLACE_EXISTING);
assertThat(a, exists());
assertThat(b, exists());
assertThat(a, hasContents("aaa"));
assertThat(b, hasContents("aaa"));
setContents(a, "a1");
assertThat(a, hasContents("a1"));
assertThat(b, hasContents("aaa"));
}
@Test
public void moveRootIntoItself() throws IOException {
Path root = this.rule.getFileSystem().getPath("/");
Files.move(root, root);
}
@Test(expected = FileSystemException.class)
public void moveRootIntoSubfolder() throws IOException {
Path dir = Files.createDirectory(this.rule.getFileSystem().getPath("/dir"));
Path sub = dir.resolve("sub");
Files.move(dir, sub);
} @Test
public void moveRoot() throws IOException {
Path root = this.rule.getFileSystem().getPath("/");
Path path = this.rule.getFileSystem().getPath("/a");
try {
Files.move(root, path);
fail("moving the root should not work");
} catch (IOException e) {
// expected
}
try {
Files.move(path, root);
fail("moving the root should not work");
} catch (IOException e) {
// expected
}
}
@Test
public void moveRootDifferentFileSystems() throws IOException {
Path firstRoot = this.rule.getFileSystem().getPath("/");
try (FileSystem second = MemoryFileSystemBuilder.newEmpty().build("second");) {
Path secondRoot = second.getPath("/");
try {
Files.move(firstRoot, secondRoot);
fail("moving the root should not work");
} catch (IOException e) {
// expected
}
try {
Files.move(secondRoot, firstRoot);
fail("moving the root should not work");
} catch (IOException e) {
// expected
}
}
}
@Test
public void moveToDifferentParent() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
// move /a/c to /b/c
Path a = fileSystem.getPath("/a");
Files.createDirectory(a);
Path b = fileSystem.getPath("/b");
Files.createDirectory(b);
Path ac = fileSystem.getPath("/a/c");
Files.createFile(ac);
Path bc = fileSystem.getPath("/b/c");
assertThat(a, exists());
assertThat(b, exists());
assertThat(ac, exists());
assertThat(bc, not(exists()));
Files.move(ac, bc);
assertThat(ac, not(exists()));
assertThat(bc, exists());
List<Path> aKids = new ArrayList<>(1);
try (DirectoryStream<Path> stream = Files.newDirectoryStream(a)) {
for (Path path : stream) {
aKids.add(path);
}
}
assertThat(aKids, empty());
List<Path> bKids = new ArrayList<>(1);
try (DirectoryStream<Path> stream = Files.newDirectoryStream(b)) {
for (Path path : stream) {
bKids.add(path);
}
}
assertEquals(bKids, Collections.singletonList(bc));
}
@Test
public void renameFile() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
// move /a/c to /b/c
Path a = fileSystem.getPath("/a");
Files.createDirectory(a);
Path ab = fileSystem.getPath("/a/b");
Files.createFile(ab);
Path ac = fileSystem.getPath("/a/c");
assertThat(a, exists());
assertThat(ab, exists());
assertThat(ac, not(exists()));
Files.move(ab, ac);
assertThat(ab, not(exists()));
assertThat(ac, exists());
List<Path> aKids = new ArrayList<>(1);
try (DirectoryStream<Path> stream = Files.newDirectoryStream(a)) {
for (Path path : stream) {
aKids.add(path);
}
}
assertEquals(Collections.singletonList(ac), aKids);
}
@Test
public void moveSameFile() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path a = fileSystem.getPath("a");
Path b = fileSystem.getPath("./b/../a");
this.createAndSetContents(a, "aaa");
assertThat(a, exists());
assertThat(b, exists());
Files.move(a, b);
assertThat(a, exists());
assertThat(b, exists());
assertThat(a, hasContents("aaa"));
assertThat(b, hasContents("aaa"));
} @Test
public void moveAlreadyExistsNotEmpty() throws IOException, ParseException {
// moving a folder to an already existing one that is not empty should throw DirectoryNotEmptyException
FileSystem fileSystem = this.rule.getFileSystem();
Path source = fileSystem.getPath("source");
Path target = fileSystem.getPath("target");
Path child = fileSystem.getPath("target/child.txt");
Files.createDirectory(source);
Files.createDirectory(target);
Files.createFile(child);
try {
Files.move(source, target, REPLACE_EXISTING);
fail("should not be able to overwrite non-empty directories");
} catch (DirectoryNotEmptyException e) {
// should reach here
assert(true);
}
}
@Test
public void moveAlreadyExists() throws IOException, ParseException {
// moving a folder to an already existing one should throw FileAlreadyExistsException
FileSystem fileSystem = this.rule.getFileSystem();
Path source = fileSystem.getPath("source");
Path target = fileSystem.getPath("target");
Files.createDirectory(source);
Files.createDirectory(target);
try {
Files.move(source, target);
fail("should not be able to overwrite exsiting directories");
} catch (FileAlreadyExistsException e) {
// should reach here
assert(true);
}
}
@Test
public void moveOverwriteExists() throws IOException, ParseException {
// moving a folder to an already existing one should work with REPLACE_EXISTING
FileSystem fileSystem = this.rule.getFileSystem();
Path source = fileSystem.getPath("source");
Path target = fileSystem.getPath("target");
Files.createDirectory(source);
Files.createDirectory(target);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
FileTime sourceTime = FileTime.fromMillis(format.parse("2012-11-07T20:30:22").getTime());
FileTime targetTime = FileTime.fromMillis(format.parse("2012-10-07T20:30:22").getTime());
Files.setLastModifiedTime(source, sourceTime);
Files.setLastModifiedTime(target, targetTime);
assertEquals(sourceTime, Files.getLastModifiedTime(source));
assertEquals(targetTime, Files.getLastModifiedTime(target));
Files.move(source, target, REPLACE_EXISTING);
assertThat(source, not(exists()));
assertThat(target, exists());
assertEquals(sourceTime, Files.getLastModifiedTime(target));
}
@Test
public void moveDifferentFileSystem() throws IOException {
FileSystem source = this.rule.getFileSystem();
try (FileSystem target = MemoryFileSystemBuilder.newEmpty().build("target")) {
Path a = source.getPath("a");
Path b = target.getPath("b");
this.createAndSetContents(a, "aaa");
assertThat(a, exists());
assertThat(b, not(exists()));
Files.move(a, b);
assertThat(a, not(exists()));
assertThat(b, exists());
assertThat(b, hasContents("aaa"));
}
}
@Test
public void moveNoExisitingNoAttributes() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path a = fileSystem.getPath("/1/a");
Path b = fileSystem.getPath("/2/b");
Files.createDirectories(b.toAbsolutePath().getParent());
this.createAndSetContents(a, "aaa");
assertThat(a, exists());
assertThat(b, not(exists()));
Files.move(a, b);
assertThat(a, not(exists()));
assertThat(b, exists());
assertThat(b, hasContents("aaa"));
} @Test
public void moveReplaceExisitingNoAttributes() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path a = fileSystem.getPath("/1/a");
Path b = fileSystem.getPath("/2/b");
this.createAndSetContents(a, "aaa");
this.createAndSetContents(b, "bbb");
assertThat(a, exists());
assertThat(b, exists());
Files.move(a, b, StandardCopyOption.REPLACE_EXISTING);
assertThat(a, not(exists()));
assertThat(b, exists());
assertThat(b, hasContents("aaa"));
}
@Test
public void moveNonEmptyFolder() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path src = fileSystem.getPath("/src");
Path target = fileSystem.getPath("/target");
Files.createDirectory(src);
Files.createFile(src.resolve("file"));
Files.move(src, target);
assertThat(src.resolve("file"), not(exists()));
assertThat(target.resolve("file"), exists());
}
@Test
public void moveSymlink() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path target = fileSystem.getPath("/target");
Files.createDirectory(target);
Path from = fileSystem.getPath("/from");
Files.createSymbolicLink(from, target);
Path to = fileSystem.getPath("/to");
Files.move(from, to);
assertThat(to, isSymbolicLink());
assertEquals(target, to.toRealPath());
}
private void createAndSetContents(Path path, String contents) throws IOException {
Path parent = path.toAbsolutePath().getParent();
if (!parent.equals(parent.getRoot())) {
Files.createDirectories(parent);
}
try (SeekableByteChannel channel = Files.newByteChannel(path, WRITE, CREATE_NEW)) {
channel.write(ByteBuffer.wrap(contents.getBytes(US_ASCII)));
}
}
}