mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-07-20 06:52:36 +00:00
Tests for filesystem-nio
* Split tests into integration and unit tests * Rewritten and completed unit tests ** By introducing a layer around Files.* to allow mocking of NIO-Operations ** And introducing a factory to allow mocking of constructors * Integration tests ignored temporarily ** TODO reduce amount of testcases and enable
This commit is contained in:
@@ -12,5 +12,12 @@
|
||||
<artifactId>commons</artifactId>
|
||||
<name>Cryptomator common</name>
|
||||
<description>Shared utilities</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>commons-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Holder<V> implements Supplier<V>, Consumer<V> {
|
||||
|
||||
private final V initial;
|
||||
|
||||
private V value;
|
||||
|
||||
public <W extends V> Holder(W initial) {
|
||||
this.initial = initial;
|
||||
reset();
|
||||
}
|
||||
|
||||
public V get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void set(V value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
set(initial);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(V value) {
|
||||
set(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class HolderTest {
|
||||
|
||||
private static final Object INITIAL = new Object();
|
||||
private static final Object VALUE = new Object();
|
||||
|
||||
private Holder<Object> inTest = new Holder<>(INITIAL);
|
||||
|
||||
@Test
|
||||
public void testInitialValueIsInitial() {
|
||||
assertThat(inTest.get(), is(INITIAL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetChangesValue() {
|
||||
inTest.set(VALUE);
|
||||
|
||||
assertThat(inTest.get(), is(VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptChangesValue() {
|
||||
inTest.accept(VALUE);
|
||||
|
||||
assertThat(inTest.get(), is(VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetChangesValueToInitial() {
|
||||
inTest.set(VALUE);
|
||||
inTest.reset();
|
||||
|
||||
assertThat(inTest.get(), is(INITIAL));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,4 +33,17 @@ public interface Node {
|
||||
|
||||
Instant lastModified() throws UncheckedIOException;
|
||||
|
||||
/**
|
||||
* @return the {@link FileSystem} this Node belongs to
|
||||
*/
|
||||
default FileSystem fileSystem() {
|
||||
return parent() //
|
||||
.map(Node::fileSystem) //
|
||||
.orElseGet(() -> (FileSystem) this);
|
||||
}
|
||||
|
||||
default boolean belongsToSameFilesystem(Node other) {
|
||||
return fileSystem() == other.fileSystem();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.Iterator;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public final class PathResolver {
|
||||
final class PathResolver {
|
||||
|
||||
private static final String DOT = ".";
|
||||
private static final String DOTDOT = "..";
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Tests -->
|
||||
<dependency>
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
|
||||
class DefaultInstanceFactory implements InstanceFactory {
|
||||
|
||||
@Override
|
||||
public NioFolder nioFolder(Optional<NioFolder> parent, Path path, NioAccess nioAccess) {
|
||||
return new NioFolder(parent, path, nioAccess, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NioFile nioFile(Optional<NioFolder> parent, Path path, NioAccess nioAccess) {
|
||||
return new NioFile(parent, path, nioAccess, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedFileChannel sharedFileChannel(Path path, NioAccess nioAccess) {
|
||||
return new SharedFileChannel(path, nioAccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WritableNioFile writableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback, NioAccess nioAccess) {
|
||||
return new WritableNioFile(fileSystem, path, channel, afterCloseCallback, nioAccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableNioFile readableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback) {
|
||||
return new ReadableNioFile(fileSystem, path, channel, afterCloseCallback);
|
||||
}
|
||||
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class DefaultNioAccess implements NioAccess {
|
||||
|
||||
@Override
|
||||
public FileChannel open(Path path, OpenOption... options) throws IOException {
|
||||
return FileChannel.open(path, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegularFile(Path path, LinkOption... options) {
|
||||
return Files.isRegularFile(path, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(Path path, LinkOption... options) {
|
||||
return Files.exists(path, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory(Path path, LinkOption... options) {
|
||||
return Files.isDirectory(path, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Path> list(Path dir) throws IOException {
|
||||
return Files.list(dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectories(Path dir, FileAttribute<?>... attrs) throws IOException {
|
||||
Files.createDirectories(dir, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime getLastModifiedTime(Path path, LinkOption... options) throws IOException {
|
||||
return Files.getLastModifiedTime(path, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Path path) throws IOException {
|
||||
Files.delete(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(FileChannel channel) throws IOException {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(Path source, Path target, CopyOption... options) throws IOException {
|
||||
Files.move(source, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModifiedTime(Path path, FileTime time) throws IOException {
|
||||
Files.setLastModifiedTime(path, time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String separator() {
|
||||
return FileSystems.getDefault().getSeparator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.cryptomator.common.Holder;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
|
||||
interface InstanceFactory {
|
||||
|
||||
public static final Holder<InstanceFactory> DEFAULT = new Holder<>(new DefaultInstanceFactory());
|
||||
|
||||
NioFolder nioFolder(Optional<NioFolder> parent, Path path, NioAccess nioAccess);
|
||||
|
||||
NioFile nioFile(Optional<NioFolder> parent, Path path, NioAccess nioAccess);
|
||||
|
||||
SharedFileChannel sharedFileChannel(Path path, NioAccess nioAccess);
|
||||
|
||||
WritableNioFile writableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback, NioAccess nioAccess);
|
||||
|
||||
ReadableNioFile readableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.cryptomator.common.Holder;
|
||||
|
||||
interface NioAccess {
|
||||
|
||||
public static final Holder<NioAccess> DEFAULT = new Holder<>(new DefaultNioAccess());
|
||||
|
||||
FileChannel open(Path path, OpenOption... options) throws IOException;
|
||||
|
||||
boolean isRegularFile(Path path, LinkOption... options);
|
||||
|
||||
boolean exists(Path path, LinkOption... options);
|
||||
|
||||
boolean isDirectory(Path childPath, LinkOption... options);
|
||||
|
||||
Stream<Path> list(Path dir) throws IOException;
|
||||
|
||||
void createDirectories(Path dir, FileAttribute<?>... attrs) throws IOException;
|
||||
|
||||
FileTime getLastModifiedTime(Path path, LinkOption... options) throws IOException;
|
||||
|
||||
void delete(Path path) throws IOException;
|
||||
|
||||
void close(FileChannel channel) throws IOException;
|
||||
|
||||
void move(Path source, Path target, CopyOption... options) throws IOException;
|
||||
|
||||
void setLastModifiedTime(Path path, FileTime time) throws IOException;
|
||||
|
||||
String separator();
|
||||
|
||||
}
|
||||
@@ -4,11 +4,9 @@ import static java.lang.String.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.cryptomator.filesystem.File;
|
||||
@@ -20,17 +18,9 @@ class NioFile extends NioNode implements File {
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
|
||||
private SharedFileChannel sharedChannel;
|
||||
|
||||
public NioFile(Optional<NioFolder> parent, Path path) {
|
||||
super(parent, path);
|
||||
sharedChannel = new SharedFileChannel(path);
|
||||
}
|
||||
|
||||
SharedFileChannel channel() {
|
||||
return sharedChannel;
|
||||
}
|
||||
|
||||
public ReadWriteLock lock() {
|
||||
return lock;
|
||||
public NioFile(Optional<NioFolder> parent, Path eventuallyNonAbsolutePath, NioAccess nioAccess, InstanceFactory instanceFactory) {
|
||||
super(parent, eventuallyNonAbsolutePath, nioAccess, instanceFactory);
|
||||
sharedChannel = instanceFactory.sharedFileChannel(path, nioAccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,7 +32,11 @@ class NioFile extends NioNode implements File {
|
||||
throw new IllegalStateException("Current thread is already reading this file");
|
||||
}
|
||||
lock.readLock().lock();
|
||||
return new ReadableNioFile(this);
|
||||
return instanceFactory.readableNioFile(fileSystem(), path, sharedChannel, this::unlockReadLock);
|
||||
}
|
||||
|
||||
private void unlockReadLock() {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,17 +48,21 @@ class NioFile extends NioNode implements File {
|
||||
throw new IllegalStateException("Current thread is currently reading this file");
|
||||
}
|
||||
lock.writeLock().lock();
|
||||
return new WritableNioFile(this);
|
||||
return instanceFactory.writableNioFile(fileSystem(), path, sharedChannel, this::unlockWriteLock, nioAccess);
|
||||
}
|
||||
|
||||
private void unlockWriteLock() {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() throws UncheckedIOException {
|
||||
return Files.isRegularFile(path);
|
||||
return nioAccess.isRegularFile(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant lastModified() throws UncheckedIOException {
|
||||
if (Files.exists(path) && !exists()) {
|
||||
if (nioAccess.exists(path) && !exists()) {
|
||||
throw new UncheckedIOException(new IOException(format("%s is a folder", path)));
|
||||
}
|
||||
return super.lastModified();
|
||||
@@ -73,7 +71,6 @@ class NioFile extends NioNode implements File {
|
||||
@Override
|
||||
public int compareTo(File o) {
|
||||
if (belongsToSameFilesystem(o)) {
|
||||
assert o instanceof NioNode;
|
||||
return path.compareTo(((NioFile) o).path);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Can not mix File objects from different file systems");
|
||||
@@ -85,8 +82,4 @@ class NioFile extends NioNode implements File {
|
||||
return format("NioFile(%s)", path);
|
||||
}
|
||||
|
||||
Path path() {
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public class NioFileSystem extends NioFolder implements FileSystem {
|
||||
}
|
||||
|
||||
private NioFileSystem(Path root) {
|
||||
super(Optional.empty(), root);
|
||||
super(Optional.empty(), root, NioAccess.DEFAULT.get(), InstanceFactory.DEFAULT.get());
|
||||
create();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.cryptomator.filesystem.FileSystemVisitor.fileSystemVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
@@ -21,21 +19,21 @@ class NioFolder extends NioNode implements Folder {
|
||||
private final WeakValuedCache<Path, NioFolder> folders = WeakValuedCache.usingLoader(this::folderFromPath);
|
||||
private final WeakValuedCache<Path, NioFile> files = WeakValuedCache.usingLoader(this::fileFromPath);
|
||||
|
||||
public NioFolder(Optional<NioFolder> parent, Path path) {
|
||||
super(parent, path);
|
||||
public NioFolder(Optional<NioFolder> parent, Path path, NioAccess nioAccess, InstanceFactory instanceFactory) {
|
||||
super(parent, path, nioAccess, instanceFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends Node> children() throws UncheckedIOException {
|
||||
try {
|
||||
return Files.list(path).map(this::childPathToNode);
|
||||
return nioAccess.list(path).map(this::childPathToNode);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private NioNode childPathToNode(Path childPath) {
|
||||
if (Files.isDirectory(childPath)) {
|
||||
if (nioAccess.isDirectory(childPath)) {
|
||||
return folders.get(childPath);
|
||||
} else {
|
||||
return files.get(childPath);
|
||||
@@ -43,27 +41,35 @@ class NioFolder extends NioNode implements Folder {
|
||||
}
|
||||
|
||||
private NioFile fileFromPath(Path path) {
|
||||
return new NioFile(Optional.of(this), path);
|
||||
return instanceFactory.nioFile(Optional.of(this), path, nioAccess);
|
||||
}
|
||||
|
||||
private NioFolder folderFromPath(Path path) {
|
||||
return new NioFolder(Optional.of(this), path);
|
||||
return instanceFactory.nioFolder(Optional.of(this), path, nioAccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File file(String name) throws UncheckedIOException {
|
||||
assertDoesNotContainsSeparator(name);
|
||||
return files.get(path.resolve(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Folder folder(String name) throws UncheckedIOException {
|
||||
assertDoesNotContainsSeparator(name);
|
||||
return folders.get(path.resolve(name));
|
||||
}
|
||||
|
||||
private void assertDoesNotContainsSeparator(String name) {
|
||||
if (name.contains(nioAccess.separator())) {
|
||||
throw new IllegalArgumentException(format("Name must not contain file system separator (name: %s, separator: %s)", name, nioAccess.separator()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() throws UncheckedIOException {
|
||||
try {
|
||||
Files.createDirectories(path);
|
||||
nioAccess.createDirectories(path);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
@@ -71,7 +77,7 @@ class NioFolder extends NioNode implements Folder {
|
||||
|
||||
@Override
|
||||
public Instant lastModified() throws UncheckedIOException {
|
||||
if (Files.exists(path) && !Files.isDirectory(path)) {
|
||||
if (nioAccess.exists(path) && !nioAccess.isDirectory(path)) {
|
||||
throw new UncheckedIOException(new IOException(format("%s is a file", path)));
|
||||
}
|
||||
return super.lastModified();
|
||||
@@ -79,7 +85,7 @@ class NioFolder extends NioNode implements Folder {
|
||||
|
||||
@Override
|
||||
public boolean exists() throws UncheckedIOException {
|
||||
return Files.isDirectory(path);
|
||||
return nioAccess.isDirectory(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,12 +101,16 @@ class NioFolder extends NioNode implements Folder {
|
||||
try {
|
||||
target.delete();
|
||||
target.parent().ifPresent(folder -> folder.create());
|
||||
Files.move(path, target.path);
|
||||
nioAccess.move(path(), target.path());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Path path() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format("NioFolder(%s)", path);
|
||||
@@ -111,10 +121,13 @@ class NioFolder extends NioNode implements Folder {
|
||||
if (!exists()) {
|
||||
return;
|
||||
}
|
||||
fileSystemVisitor() //
|
||||
.forEachFile(NioFolder::deleteFile) //
|
||||
.afterFolder(NioFolder::deleteEmptyFolder) //
|
||||
.visit(this);
|
||||
folders().forEach(Folder::delete);
|
||||
files().forEach(NioFolder::deleteFile);
|
||||
try {
|
||||
nioAccess.delete(path);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final void deleteFile(File file) {
|
||||
@@ -123,12 +136,4 @@ class NioFolder extends NioNode implements Folder {
|
||||
}
|
||||
}
|
||||
|
||||
private static final void deleteEmptyFolder(Folder folder) {
|
||||
try {
|
||||
Files.delete(((NioFolder) folder).path);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.cryptomator.filesystem.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
@@ -15,25 +14,14 @@ abstract class NioNode implements Node {
|
||||
protected final Optional<NioFolder> parent;
|
||||
protected final Path path;
|
||||
|
||||
private NioFileSystem fileSystem;
|
||||
protected final NioAccess nioAccess;
|
||||
protected final InstanceFactory instanceFactory;
|
||||
|
||||
public NioNode(Optional<NioFolder> parent, Path path) {
|
||||
public NioNode(Optional<NioFolder> parent, Path path, NioAccess nioAccess, InstanceFactory instanceFactoy) {
|
||||
this.path = path.toAbsolutePath();
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
NioFileSystem fileSystem() {
|
||||
if (fileSystem == null) {
|
||||
fileSystem = parent //
|
||||
.map(NioNode::fileSystem) //
|
||||
.orElseGet(() -> (NioFileSystem) this);
|
||||
}
|
||||
return fileSystem;
|
||||
}
|
||||
|
||||
boolean belongsToSameFilesystem(Node other) {
|
||||
return other instanceof NioNode //
|
||||
&& ((NioNode) other).fileSystem() == fileSystem();
|
||||
this.nioAccess = nioAccess;
|
||||
this.instanceFactory = instanceFactoy;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,7 +37,7 @@ abstract class NioNode implements Node {
|
||||
@Override
|
||||
public Instant lastModified() throws UncheckedIOException {
|
||||
try {
|
||||
return Files.getLastModifiedTime(path).toInstant();
|
||||
return nioAccess.getLastModifiedTime(path).toInstant();
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
|
||||
+19
-11
@@ -6,26 +6,34 @@ import static org.cryptomator.filesystem.nio.OpenMode.READ;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
|
||||
class ReadableNioFile implements ReadableFile {
|
||||
|
||||
private final NioFile nioFile;
|
||||
private final FileSystem fileSystem;
|
||||
private final Path path;
|
||||
private final SharedFileChannel channel;
|
||||
private final Runnable afterCloseCallback;
|
||||
|
||||
private boolean open = true;
|
||||
private long position = 0;
|
||||
|
||||
public ReadableNioFile(NioFile nioFile) {
|
||||
this.nioFile = nioFile;
|
||||
nioFile.channel().open(READ);
|
||||
public ReadableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.path = path;
|
||||
this.channel = channel;
|
||||
this.afterCloseCallback = afterCloseCallback;
|
||||
channel.open(READ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer target) throws UncheckedIOException {
|
||||
assertOpen();
|
||||
int read = nioFile.channel().readFully(position, target);
|
||||
int read = channel.readFully(position, target);
|
||||
if (read != SharedFileChannel.EOF) {
|
||||
position += read;
|
||||
}
|
||||
@@ -57,7 +65,7 @@ class ReadableNioFile implements ReadableFile {
|
||||
}
|
||||
|
||||
private boolean belongsToSameFilesystem(WritableFile other) {
|
||||
return other instanceof WritableNioFile && ((WritableNioFile) other).nioFile().belongsToSameFilesystem(nioFile);
|
||||
return other instanceof WritableNioFile && ((WritableNioFile) other).fileSystem() == fileSystem;
|
||||
}
|
||||
|
||||
private void internalCopyTo(WritableNioFile target) {
|
||||
@@ -65,10 +73,10 @@ class ReadableNioFile implements ReadableFile {
|
||||
target.ensureChannelIsOpened();
|
||||
SharedFileChannel targetChannel = target.channel();
|
||||
targetChannel.truncate(0);
|
||||
long size = nioFile.channel().size();
|
||||
long size = channel.size();
|
||||
long transferred = 0;
|
||||
while (transferred < size) {
|
||||
transferred += nioFile.channel().transferTo(transferred, size - transferred, targetChannel, transferred);
|
||||
transferred += channel.transferTo(transferred, size - transferred, targetChannel, transferred);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +87,9 @@ class ReadableNioFile implements ReadableFile {
|
||||
}
|
||||
open = false;
|
||||
try {
|
||||
nioFile.channel().close();
|
||||
channel.close();
|
||||
} finally {
|
||||
nioFile.lock().readLock().unlock();
|
||||
afterCloseCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +101,7 @@ class ReadableNioFile implements ReadableFile {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format("Readable%s", nioFile);
|
||||
return format("ReadableNioFile(%s)", path);
|
||||
}
|
||||
|
||||
}
|
||||
+42
-33
@@ -1,9 +1,12 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Map;
|
||||
@@ -16,21 +19,29 @@ class SharedFileChannel {
|
||||
public static final int EOF = -1;
|
||||
|
||||
private final Path path;
|
||||
private final NioAccess nioAccess;
|
||||
|
||||
private Map<Thread, Thread> openedBy = new ConcurrentHashMap<>();
|
||||
private Lock lock = new ReentrantLock();
|
||||
|
||||
private FileChannel delegate;
|
||||
|
||||
public SharedFileChannel(Path path) {
|
||||
public SharedFileChannel(Path path, NioAccess nioAccess) {
|
||||
this.path = path;
|
||||
this.nioAccess = nioAccess;
|
||||
}
|
||||
|
||||
public void openIfClosed(OpenMode mode) {
|
||||
if (!openedBy.containsKey(Thread.currentThread())) {
|
||||
open(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public void open(OpenMode mode) {
|
||||
doLocked(() -> {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (openedBy.put(thread, thread) != null) {
|
||||
throw new IllegalStateException("A thread can only open a SharedFileChannel once");
|
||||
throw new IllegalStateException("SharedFileChannel already open for current thread");
|
||||
}
|
||||
if (delegate == null) {
|
||||
createChannel(mode);
|
||||
@@ -38,8 +49,18 @@ class SharedFileChannel {
|
||||
});
|
||||
}
|
||||
|
||||
public void closeIfOpen() {
|
||||
if (openedBy.containsKey(Thread.currentThread())) {
|
||||
internalClose();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
assertOpenedByCurrentThread();
|
||||
internalClose();
|
||||
}
|
||||
|
||||
private void internalClose() {
|
||||
doLocked(() -> {
|
||||
openedBy.remove(Thread.currentThread());
|
||||
try {
|
||||
@@ -54,14 +75,6 @@ class SharedFileChannel {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated only intended to be used in tests
|
||||
*/
|
||||
@Deprecated
|
||||
void forceClose() {
|
||||
closeSilently(delegate);
|
||||
}
|
||||
|
||||
private void assertOpenedByCurrentThread() {
|
||||
if (!openedBy.containsKey(Thread.currentThread())) {
|
||||
throw new IllegalStateException("SharedFileChannel closed for current thread");
|
||||
@@ -70,30 +83,23 @@ class SharedFileChannel {
|
||||
|
||||
private void createChannel(OpenMode mode) {
|
||||
try {
|
||||
FileChannel readChannel = null;
|
||||
if (mode == OpenMode.READ) {
|
||||
readChannel = FileChannel.open(path, StandardOpenOption.READ);
|
||||
if (nioAccess.isDirectory(path)) {
|
||||
throw new IOException(format("%s is a directory", path));
|
||||
}
|
||||
delegate = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
closeSilently(readChannel);
|
||||
if (mode == OpenMode.READ) {
|
||||
if (!nioAccess.isRegularFile(path)) {
|
||||
throw new NoSuchFileException(format("%s does not exist", path));
|
||||
}
|
||||
}
|
||||
delegate = nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeSilently(FileChannel channel) {
|
||||
if (channel != null) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeChannel() {
|
||||
try {
|
||||
delegate.close();
|
||||
nioAccess.close(delegate);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} finally {
|
||||
@@ -146,15 +152,18 @@ class SharedFileChannel {
|
||||
public long transferTo(long position, long count, SharedFileChannel targetChannel, long targetPosition) {
|
||||
assertOpenedByCurrentThread();
|
||||
targetChannel.assertOpenedByCurrentThread();
|
||||
if (count < 0) {
|
||||
throw new IllegalArgumentException("Count must not be negative");
|
||||
}
|
||||
try {
|
||||
long maxPosition = delegate.size();
|
||||
long maxCount = Math.min(count, maxPosition - position);
|
||||
long remaining = maxCount;
|
||||
long maxPosition = Math.min(delegate.size(), position + count);
|
||||
long transferCount = Math.max(0, maxPosition - position);
|
||||
long remaining = transferCount;
|
||||
targetChannel.delegate.position(targetPosition);
|
||||
while (remaining > 0) {
|
||||
remaining -= delegate.transferTo(maxPosition - remaining, remaining, targetChannel.delegate);
|
||||
}
|
||||
return maxCount;
|
||||
return transferCount;
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
@@ -179,12 +188,12 @@ class SharedFileChannel {
|
||||
}
|
||||
|
||||
private int tryWriteFully(long position, ByteBuffer source) throws IOException {
|
||||
int initialRemaining = source.remaining();
|
||||
long maxPosition = position + initialRemaining;
|
||||
int count = source.remaining();
|
||||
long maxPosition = position + count;
|
||||
do {
|
||||
delegate.write(source, maxPosition - source.remaining());
|
||||
} while (source.hasRemaining());
|
||||
return initialRemaining - source.remaining();
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+40
-34
@@ -8,30 +8,37 @@ import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
|
||||
class WritableNioFile implements WritableFile {
|
||||
|
||||
private final NioFile nioFile;
|
||||
private final FileSystem fileSystem;
|
||||
private final Path path;
|
||||
private final SharedFileChannel channel;
|
||||
private final NioAccess nioAccess;
|
||||
private Runnable afterCloseCallback;
|
||||
|
||||
private boolean channelOpened = false;
|
||||
private boolean open = true;
|
||||
private long position = 0;
|
||||
|
||||
public WritableNioFile(NioFile nioFile) {
|
||||
this.nioFile = nioFile;
|
||||
public WritableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback, NioAccess nioAccess) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.path = path;
|
||||
this.channel = channel;
|
||||
this.afterCloseCallback = afterCloseCallback;
|
||||
this.nioAccess = nioAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer source) throws UncheckedIOException {
|
||||
assertOpen();
|
||||
ensureChannelIsOpened();
|
||||
int written = nioFile.channel().writeFully(position, source);
|
||||
int written = channel.writeFully(position, source);
|
||||
position += written;
|
||||
return written;
|
||||
}
|
||||
@@ -48,7 +55,7 @@ class WritableNioFile implements WritableFile {
|
||||
}
|
||||
|
||||
private boolean belongsToSameFilesystem(WritableFile other) {
|
||||
return other instanceof WritableNioFile && ((WritableNioFile) other).nioFile().belongsToSameFilesystem(nioFile);
|
||||
return other instanceof WritableNioFile && ((WritableNioFile) other).fileSystem() == fileSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,27 +72,27 @@ class WritableNioFile implements WritableFile {
|
||||
|
||||
private void internalMoveTo(WritableNioFile other) {
|
||||
other.assertOpen();
|
||||
assertMovePreconditionsAreMet(other);
|
||||
try {
|
||||
assertMovePreconditionsAreMet(other);
|
||||
closeChannelIfOpened();
|
||||
other.closeChannelIfOpened();
|
||||
Files.move(path(), other.path(), REPLACE_EXISTING);
|
||||
nioAccess.move(path, other.path(), REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} finally {
|
||||
open = false;
|
||||
other.open = false;
|
||||
other.nioFile.lock().writeLock().unlock();
|
||||
nioFile.lock().writeLock().unlock();
|
||||
other.invokeAfterCloseCallback();
|
||||
invokeAfterCloseCallback();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMovePreconditionsAreMet(WritableNioFile other) {
|
||||
if (Files.isDirectory(path())) {
|
||||
throw new UncheckedIOException(new IOException(format("Can not move %s to %s. Source is a directory", path(), other.path())));
|
||||
if (nioAccess.isDirectory(path)) {
|
||||
throw new UncheckedIOException(new IOException(format("Can not move %s to %s. Source is a directory", path, other.path())));
|
||||
}
|
||||
if (Files.isDirectory(other.path())) {
|
||||
throw new UncheckedIOException(new IOException(format("Can not move %s to %s. Target is a directory", path(), other.path())));
|
||||
if (nioAccess.isDirectory(other.path())) {
|
||||
throw new UncheckedIOException(new IOException(format("Can not move %s to %s. Target is a directory", path, other.path())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +101,7 @@ class WritableNioFile implements WritableFile {
|
||||
assertOpen();
|
||||
ensureChannelIsOpened();
|
||||
try {
|
||||
Files.setLastModifiedTime(path(), FileTime.from(instant));
|
||||
nioAccess.setLastModifiedTime(path, FileTime.from(instant));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
@@ -105,12 +112,12 @@ class WritableNioFile implements WritableFile {
|
||||
assertOpen();
|
||||
try {
|
||||
closeChannelIfOpened();
|
||||
Files.delete(nioFile.path());
|
||||
nioAccess.delete(path);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} finally {
|
||||
open = false;
|
||||
nioFile.lock().writeLock().unlock();
|
||||
invokeAfterCloseCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +125,7 @@ class WritableNioFile implements WritableFile {
|
||||
public void truncate() throws UncheckedIOException {
|
||||
assertOpen();
|
||||
ensureChannelIsOpened();
|
||||
nioFile.channel().truncate(0);
|
||||
channel.truncate(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,33 +137,32 @@ class WritableNioFile implements WritableFile {
|
||||
try {
|
||||
closeChannelIfOpened();
|
||||
} finally {
|
||||
nioFile.lock().writeLock().unlock();
|
||||
afterCloseCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
void ensureChannelIsOpened() {
|
||||
if (!channelOpened) {
|
||||
nioFile.channel().open(WRITE);
|
||||
channelOpened = true;
|
||||
}
|
||||
channel.openIfClosed(WRITE);
|
||||
}
|
||||
|
||||
private void closeChannelIfOpened() {
|
||||
if (channelOpened) {
|
||||
channel().close();
|
||||
}
|
||||
void closeChannelIfOpened() {
|
||||
channel.closeIfOpen();
|
||||
}
|
||||
|
||||
SharedFileChannel channel() {
|
||||
return nioFile.channel();
|
||||
FileSystem fileSystem() {
|
||||
return fileSystem;
|
||||
}
|
||||
|
||||
Path path() {
|
||||
return nioFile.path;
|
||||
return path;
|
||||
}
|
||||
|
||||
NioFile nioFile() {
|
||||
return nioFile;
|
||||
SharedFileChannel channel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
void invokeAfterCloseCallback() {
|
||||
afterCloseCallback.run();
|
||||
}
|
||||
|
||||
void assertOpen() {
|
||||
@@ -167,7 +173,7 @@ class WritableNioFile implements WritableFile {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format("Writable%s", this.nioFile);
|
||||
return format("WritableNioFile(%s)", path);
|
||||
}
|
||||
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DefaultInstanceFactoryTest {
|
||||
|
||||
private NioFileSystem fileSystem;
|
||||
private Optional<NioFolder> parent;
|
||||
private Path path;
|
||||
private NioAccess nioAccess;
|
||||
private SharedFileChannel channel;
|
||||
|
||||
private DefaultInstanceFactory inTest = new DefaultInstanceFactory();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
fileSystem = mock(NioFileSystem.class);
|
||||
parent = Optional.of(fileSystem);
|
||||
path = mock(Path.class);
|
||||
channel = mock(SharedFileChannel.class);
|
||||
when(path.toAbsolutePath()).thenReturn(path);
|
||||
nioAccess = mock(NioAccess.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNioFolderCreatesNioFolder() {
|
||||
NioFolder result = inTest.nioFolder(parent, path, nioAccess);
|
||||
|
||||
assertThat(result.parent(), is(parent));
|
||||
assertThat(result.path(), is(path));
|
||||
|
||||
result.exists();
|
||||
verify(nioAccess).isDirectory(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNioFileCreatesNioFile() {
|
||||
NioFile result = inTest.nioFile(parent, path, nioAccess);
|
||||
|
||||
assertThat(result.parent(), is(parent));
|
||||
|
||||
result.exists();
|
||||
verify(nioAccess).isRegularFile(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedFileChannelCreatesSharedFileChannel() throws IOException {
|
||||
SharedFileChannel result = inTest.sharedFileChannel(path, nioAccess);
|
||||
|
||||
result.open(OpenMode.WRITE);
|
||||
verify(nioAccess).open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritableNioFileCreatesWritableNioFile() throws IOException {
|
||||
Runnable afterCloseCallback = mock(Runnable.class);
|
||||
WritableNioFile result = inTest.writableNioFile(fileSystem, path, channel, afterCloseCallback, nioAccess);
|
||||
|
||||
assertThat(result.path(), is(path));
|
||||
assertThat(result.channel(), is(channel));
|
||||
assertThat(result.fileSystem(), is(fileSystem));
|
||||
|
||||
result.delete();
|
||||
verify(nioAccess).delete(path);
|
||||
verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadableNioFileCreatesWritableNioFile() throws IOException {
|
||||
Runnable afterCloseCallback = mock(Runnable.class);
|
||||
ReadableNioFile result = inTest.readableNioFile(fileSystem, path, channel, afterCloseCallback);
|
||||
|
||||
assertThat(result.toString(), is(format("ReadableNioFile(%s)", path)));
|
||||
|
||||
result.close();
|
||||
verify(channel).close();
|
||||
verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.nio.file.StandardCopyOption.ATOMIC_MOVE;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.cryptomator.common.test.matcher.ContainsMatcher.contains;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.same;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
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.spi.FileSystemProvider;
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DefaultNioAccessTest {
|
||||
|
||||
private DefaultNioAccess inTest = new DefaultNioAccess();
|
||||
|
||||
private Path path;
|
||||
private Path otherPath;
|
||||
|
||||
private FileSystem fileSystem;
|
||||
private FileSystemProvider provider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
path = mock(Path.class);
|
||||
otherPath = mock(Path.class);
|
||||
fileSystem = mock(FileSystem.class);
|
||||
provider = mock(FileSystemProvider.class);
|
||||
|
||||
when(fileSystem.provider()).thenReturn(provider);
|
||||
when(path.getFileSystem()).thenReturn(fileSystem);
|
||||
when(otherPath.getFileSystem()).thenReturn(fileSystem);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenCallsOpenOnProvider() throws IOException {
|
||||
OpenOption[] options = {StandardOpenOption.APPEND};
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
when(provider.newFileChannel(path, new HashSet<>(asList(options)))).thenReturn(channel);
|
||||
|
||||
FileChannel result = inTest.open(path, options);
|
||||
|
||||
assertThat(result, is(channel));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsRegularFileCallsIsRegularFileOnProvider() throws IOException {
|
||||
LinkOption[] options = {LinkOption.NOFOLLOW_LINKS};
|
||||
BasicFileAttributes attributes = mock(BasicFileAttributes.class);
|
||||
when(attributes.isRegularFile()).thenReturn(true);
|
||||
when(provider.readAttributes(path, BasicFileAttributes.class, options)).thenReturn(attributes);
|
||||
|
||||
boolean result = inTest.isRegularFile(path, options);
|
||||
|
||||
assertThat(result, is(true));
|
||||
verify(attributes).isRegularFile();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsDirectoryCallsIsDirectoryOnProvider() throws IOException {
|
||||
LinkOption[] options = {LinkOption.NOFOLLOW_LINKS};
|
||||
BasicFileAttributes attributes = mock(BasicFileAttributes.class);
|
||||
when(attributes.isDirectory()).thenReturn(true);
|
||||
when(provider.readAttributes(path, BasicFileAttributes.class, options)).thenReturn(attributes);
|
||||
|
||||
boolean result = inTest.isDirectory(path, options);
|
||||
|
||||
assertThat(result, is(true));
|
||||
verify(attributes).isDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsCallsProviderCheckAccessAndReturnsTrueIfItWorks() throws IOException {
|
||||
boolean result = inTest.exists(path);
|
||||
|
||||
assertThat(result, is(true));
|
||||
verify(provider).checkAccess(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsCallsProviderCheckAccessAndReturnsFalseIfItThrowsAFileNotFoundException() throws IOException {
|
||||
doThrow(new FileNotFoundException()).when(provider).checkAccess(path);
|
||||
|
||||
boolean result = inTest.exists(path);
|
||||
|
||||
assertThat(result, is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListReturnsStreamCreatedFromDirectoryStream() throws IOException {
|
||||
@SuppressWarnings("unchecked")
|
||||
DirectoryStream<Path> directoryStream = mock(DirectoryStream.class);
|
||||
Path aPath = mock(Path.class);
|
||||
Path anotherPath = mock(Path.class);
|
||||
when(directoryStream.iterator()).thenReturn(asList(aPath, anotherPath).iterator());
|
||||
when(provider.newDirectoryStream(same(path), any())).thenReturn(directoryStream);
|
||||
|
||||
assertThat(inTest.list(path).collect(toList()), contains(is(aPath), is(anotherPath)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDirectoriesCreatesDirectoryUsingProvider() throws IOException {
|
||||
FileAttribute<?>[] attributes = {mock(FileAttribute.class)};
|
||||
|
||||
inTest.createDirectories(path, attributes);
|
||||
|
||||
verify(provider).createDirectory(path, attributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLastModifiedTimeGetsLastModifiedTimeUsingProvider() throws IOException {
|
||||
LinkOption[] options = {LinkOption.NOFOLLOW_LINKS};
|
||||
FileTime expectedResult = FileTime.from(Instant.now());
|
||||
BasicFileAttributes attributes = mock(BasicFileAttributes.class);
|
||||
when(attributes.lastModifiedTime()).thenReturn(expectedResult);
|
||||
when(provider.readAttributes(path, BasicFileAttributes.class, options)).thenReturn(attributes);
|
||||
|
||||
FileTime result = inTest.getLastModifiedTime(path, options);
|
||||
|
||||
assertThat(result, is(expectedResult));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetLastModifiedTimeSetsLastModifiedTimeUsingProvider() throws IOException {
|
||||
FileTime fileTime = FileTime.from(Instant.now());
|
||||
BasicFileAttributeView attributes = mock(BasicFileAttributeView.class);
|
||||
when(provider.getFileAttributeView(path, BasicFileAttributeView.class)).thenReturn(attributes);
|
||||
|
||||
inTest.setLastModifiedTime(path, fileTime);
|
||||
|
||||
verify(attributes).setTimes(fileTime, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteDeletesUsingProvider() throws IOException {
|
||||
inTest.delete(path);
|
||||
|
||||
verify(provider).delete(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteMovesUsingProvider() throws IOException {
|
||||
CopyOption[] options = {ATOMIC_MOVE};
|
||||
inTest.move(path, otherPath, options);
|
||||
|
||||
verify(provider).move(path, otherPath, options);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseInvokesCloseOnChannel() throws IOException {
|
||||
Runnable implCloseChannel = mock(Runnable.class);
|
||||
FileChannel channel = new FileChannelAdapter() {
|
||||
@Override
|
||||
public void implCloseChannel() throws IOException {
|
||||
implCloseChannel.run();
|
||||
}
|
||||
};
|
||||
|
||||
inTest.close(channel);
|
||||
|
||||
verify(implCloseChannel).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeparatorReturnsSeparatorOfDefaultFileSystem() {
|
||||
assertThat(inTest.separator(), is(FileSystems.getDefault().getSeparator()));
|
||||
}
|
||||
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
|
||||
class FileChannelAdapter extends FileChannel {
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileChannel position(long newPosition) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileChannel truncate(long size) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void force(boolean metaData) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dst, long position) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer src, long position) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileLock lock(long position, long size, boolean shared) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void implCloseChannel() throws IOException {
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class InstanceFactoryTest {
|
||||
|
||||
@Test
|
||||
public void testInitialDefaultIsDefaultInstanceFactory() {
|
||||
InstanceFactory.DEFAULT.reset();
|
||||
|
||||
assertThat(InstanceFactory.DEFAULT.get(), is(instanceOf(DefaultInstanceFactory.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class NioAccessTest {
|
||||
|
||||
@Test
|
||||
public void testInitialDefaultIsDefaultNioAccess() {
|
||||
NioAccess.DEFAULT.reset();
|
||||
|
||||
assertThat(NioAccess.DEFAULT.get(), is(instanceOf(DefaultNioAccess.class)));
|
||||
}
|
||||
|
||||
}
|
||||
+36
-28
@@ -1,52 +1,60 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.emptyFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.file;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.testFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.PathMatcher.isDirectory;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
public class NioFileSystemTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
private Path path;
|
||||
|
||||
@Test
|
||||
public void testParentIsEmpty() throws IOException {
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
private InstanceFactory instanceFactory;
|
||||
|
||||
assertThat(fileSystem.parent().isPresent(), is(false));
|
||||
private NioAccess nioAccess;
|
||||
|
||||
private NioFileSystem inTest;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
path = mock(Path.class);
|
||||
instanceFactory = mock(InstanceFactory.class);
|
||||
nioAccess = mock(NioAccess.class);
|
||||
|
||||
when(path.toAbsolutePath()).thenReturn(path);
|
||||
|
||||
InstanceFactory.DEFAULT.set(instanceFactory);
|
||||
NioAccess.DEFAULT.set(nioAccess);
|
||||
|
||||
inTest = NioFileSystem.rootedAt(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObtainingAFileSystemWithNonExistingRootCreatesItIncludingAllParentFolders() throws IOException {
|
||||
Path emptyFilesystem = emptyFilesystem();
|
||||
Path nonExistingFilesystemRoot = emptyFilesystem.resolve("nonExistingRoot");
|
||||
Files.delete(emptyFilesystem);
|
||||
|
||||
NioFileSystem.rootedAt(nonExistingFilesystemRoot);
|
||||
|
||||
assertThat(nonExistingFilesystemRoot, isDirectory());
|
||||
public void testRootedAtCreatesNioFileSystemWithPath() {
|
||||
assertThat(inTest.instanceFactory, is(instanceFactory));
|
||||
assertThat(inTest.path, is(path));
|
||||
assertThat(inTest.nioAccess, is(nioAccess));
|
||||
assertThat(inTest.parent, is(Optional.empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObtainingAFileSystemWhooseRootIsAFileFails() throws IOException {
|
||||
Path emptyFilesystem = testFilesystem(file("rootWhichIsAFile"));
|
||||
Path rootWhichIsAFile = emptyFilesystem.resolve("rootWhichIsAFile");
|
||||
public void testRootedAtCreatesFolder() throws IOException {
|
||||
verify(nioAccess).createDirectories(path);
|
||||
}
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
|
||||
NioFileSystem.rootedAt(rootWhichIsAFile);
|
||||
@After
|
||||
public void tearDown() {
|
||||
InstanceFactory.DEFAULT.reset();
|
||||
NioAccess.DEFAULT.reset();
|
||||
}
|
||||
|
||||
}
|
||||
+274
-565
@@ -1,37 +1,31 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.cryptomator.common.test.matcher.OptionalMatcher.presentOptionalWithValueThat;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.emptyFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.file;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.folder;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.testFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.PathMatcher.doesNotExist;
|
||||
import static org.cryptomator.filesystem.nio.PathMatcher.isFile;
|
||||
import static org.cryptomator.filesystem.nio.ThreadStackMatcher.stackContains;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.rules.Timeout;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import de.bechte.junit.runners.context.HierarchicalContextRunner;
|
||||
|
||||
@@ -41,564 +35,279 @@ public class NioFileTest {
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testExistsForExistingFileReturnsTrue() {
|
||||
File existingFile = NioFileSystem.rootedAt(testFilesystem(file("testFile"))) //
|
||||
.file("testFile");
|
||||
private NioFileSystem fileSystem;
|
||||
|
||||
private Optional<NioFolder> parent;
|
||||
|
||||
private Path path;
|
||||
private NioAccess nioAccess;
|
||||
private InstanceFactory instanceFactory;
|
||||
|
||||
private SharedFileChannel channel;
|
||||
|
||||
private NioFile inTest;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
fileSystem = mock(NioFileSystem.class);
|
||||
path = mock(Path.class);
|
||||
nioAccess = mock(NioAccess.class);
|
||||
instanceFactory = mock(InstanceFactory.class);
|
||||
channel = mock(SharedFileChannel.class);
|
||||
parent = Optional.of(fileSystem);
|
||||
|
||||
Path maybeNonAbsolutePath = mock(Path.class);
|
||||
when(maybeNonAbsolutePath.toAbsolutePath()).thenReturn(path);
|
||||
|
||||
when(fileSystem.fileSystem()).thenReturn(fileSystem);
|
||||
when(instanceFactory.sharedFileChannel(path, nioAccess)).thenReturn(channel);
|
||||
|
||||
inTest = new NioFile(parent, maybeNonAbsolutePath, nioAccess, instanceFactory);
|
||||
}
|
||||
|
||||
public class Constructor {
|
||||
|
||||
@Test
|
||||
public void testConstructorCreatesASharedFileChannelFromAbsolutePathAndNioAccessUsingTheInstanceFactory() {
|
||||
verify(instanceFactory).sharedFileChannel(path, nioAccess);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorSetsParentPassedToIt() {
|
||||
assertThat(inTest.parent(), is(parent));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Open {
|
||||
|
||||
@Test
|
||||
public void testOpenReadableCreatesReadableNioFileFromNioFile() {
|
||||
ReadableNioFile readableNioFile = mock(ReadableNioFile.class);
|
||||
when(instanceFactory.readableNioFile(same(fileSystem), same(path), same(channel), any())).thenReturn(readableNioFile);
|
||||
|
||||
ReadableFile readableFile = inTest.openReadable();
|
||||
|
||||
assertThat(readableFile, is(readableNioFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableInvokedBeforeInvokingAfterCloseOperationThrowsIllegalStateException() {
|
||||
inTest.openReadable();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("already reading this file");
|
||||
|
||||
inTest.openReadable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableInvokedAfterAfterCloseOperationCreatesNewReadableFile() {
|
||||
ReadableNioFile readableNioFile = mock(ReadableNioFile.class);
|
||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
||||
when(instanceFactory.readableNioFile(same(fileSystem), same(path), same(channel), captor.capture())).thenReturn(null, readableNioFile);
|
||||
inTest.openReadable();
|
||||
captor.getValue().run();
|
||||
|
||||
ReadableFile readableFile = inTest.openReadable();
|
||||
|
||||
assertThat(readableFile, is(readableNioFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableInvokedBeforeInvokingAfterCloseOperationOfOpenWritableThrowsIllegalStateException() {
|
||||
inTest.openWritable();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("currently writing this file");
|
||||
|
||||
inTest.openReadable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableInvokedAfterInvokingAfterCloseOperationWorks() {
|
||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
||||
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), captor.capture(), same(nioAccess))).thenReturn(null);
|
||||
inTest.openWritable();
|
||||
captor.getValue().run();
|
||||
|
||||
inTest.openReadable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableCreatesWritableNioFileFromNioFileAndNioAccessUsingInstanceFactory() {
|
||||
WritableNioFile writableNioFile = mock(WritableNioFile.class);
|
||||
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), any(), same(nioAccess))).thenReturn(writableNioFile);
|
||||
|
||||
WritableFile writableFile = inTest.openWritable();
|
||||
|
||||
assertThat(writableFile, is(writableNioFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableInvokedAfterAfterCloseOperationCreatesNewWritableFile() {
|
||||
WritableNioFile writableNioFile = mock(WritableNioFile.class);
|
||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
||||
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), captor.capture(), same(nioAccess))).thenReturn(null, writableNioFile);
|
||||
inTest.openWritable();
|
||||
captor.getValue().run();
|
||||
|
||||
WritableFile writableFile = inTest.openWritable();
|
||||
|
||||
assertThat(writableFile, is(writableNioFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableInvokedBeforeInvokingAfterCloseOperationThrowsIllegalStateException() {
|
||||
inTest.openWritable();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("already writing this file");
|
||||
|
||||
inTest.openWritable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableInvokedBeforeInvokingAfterCloseOperationFromOpenReadableThrowsIllegalStateException() {
|
||||
inTest.openReadable();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("currently reading this file");
|
||||
|
||||
inTest.openWritable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableInvokedAfterInvokingAfterCloseOperationWorks() {
|
||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
||||
when(instanceFactory.readableNioFile(same(fileSystem), same(path), same(channel), captor.capture())).thenReturn(null);
|
||||
inTest.openReadable();
|
||||
captor.getValue().run();
|
||||
|
||||
inTest.openWritable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Exists {
|
||||
|
||||
@Test
|
||||
public void testExistsReturnsTrueIfPathIsRegularFile() {
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
|
||||
assertThat(inTest.exists(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsReturnsFalseIfPathIsntRegularFile() {
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
|
||||
assertThat(inTest.exists(), is(false));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class LastModified {
|
||||
|
||||
@Test
|
||||
public void testLastModifiedReturnsLastModifiedTimeForExistingFile() throws IOException {
|
||||
Instant instant = Instant.parse("2016-01-04T01:24:32Z");
|
||||
when(nioAccess.exists(path)).thenReturn(true);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
when(nioAccess.getLastModifiedTime(path)).thenReturn(FileTime.from(instant));
|
||||
|
||||
Instant result = inTest.lastModified();
|
||||
|
||||
assertThat(result, is(instant));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedInvokesGetLastModifiedTimeForNonExistingFile() throws IOException {
|
||||
Instant instant = Instant.parse("2016-01-04T01:24:32Z");
|
||||
when(nioAccess.exists(path)).thenReturn(false);
|
||||
when(nioAccess.getLastModifiedTime(path)).thenReturn(FileTime.from(instant));
|
||||
|
||||
Instant result = inTest.lastModified();
|
||||
|
||||
assertThat(result, is(instant));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedWrapsIOExceptionThrownByGetLastModifiedTimeInUncheckedIOException() throws IOException {
|
||||
IOException exceptionThrownFromGetLastModifiedTime = new IOException();
|
||||
when(nioAccess.exists(path)).thenReturn(true);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
when(nioAccess.getLastModifiedTime(path)).thenThrow(exceptionThrownFromGetLastModifiedTime);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionThrownFromGetLastModifiedTime));
|
||||
|
||||
inTest.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedThrowsUncheckedIOExceptionIfPathExistsButIsNoRegularFile() {
|
||||
when(nioAccess.exists(path)).thenReturn(true);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(format("%s is a folder", path));
|
||||
|
||||
inTest.lastModified();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CompareTo {
|
||||
|
||||
private Path otherPath;
|
||||
private NioFile otherInTest;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
otherPath = mock(Path.class);
|
||||
|
||||
Path maybeNonAbsolutePath = mock(Path.class);
|
||||
when(maybeNonAbsolutePath.toAbsolutePath()).thenReturn(otherPath);
|
||||
|
||||
otherInTest = new NioFile(parent, maybeNonAbsolutePath, nioAccess, instanceFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareToFileFromOtherFileSystemThrowsIllegalArgumentException() {
|
||||
File other = mock(File.class);
|
||||
when(other.fileSystem()).thenReturn(mock(FileSystem.class));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
|
||||
inTest.compareTo(other);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareToReturnsResultOfPathsCompareTo() {
|
||||
int expectedResult = 2873;
|
||||
when(path.compareTo(otherPath)).thenReturn(expectedResult);
|
||||
|
||||
int result = inTest.compareTo(otherInTest);
|
||||
|
||||
assertThat(result, is(expectedResult));
|
||||
}
|
||||
|
||||
assertThat(existingFile.exists(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsForNonExistingFileReturnsFalse() {
|
||||
File nonExistingFile = NioFileSystem.rootedAt(emptyFilesystem()) //
|
||||
.file("testFile");
|
||||
public void testNameReturnsFileNameOfPath() {
|
||||
Path fileName = mock(Path.class);
|
||||
when(path.getFileName()).thenReturn(fileName);
|
||||
|
||||
assertThat(nonExistingFile.exists(), is(false));
|
||||
}
|
||||
String name = inTest.name();
|
||||
|
||||
@Test
|
||||
public void testExistsForFileWhichIsAFolderReturnsFalse() {
|
||||
File fileWhichIsAFolder = NioFileSystem.rootedAt(testFilesystem(folder("nameOfAnExistingFolder"))) //
|
||||
.file("nameOfAnExistingFolder");
|
||||
|
||||
assertThat(fileWhichIsAFolder.exists(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedForExistingFileReturnsLastModifiedValue() {
|
||||
Instant expectedLastModified = Instant.parse("2015-12-31T15:03:34Z");
|
||||
File existingFile = NioFileSystem
|
||||
.rootedAt(testFilesystem( //
|
||||
file("testFile").withLastModified(expectedLastModified))) //
|
||||
.file("testFile");
|
||||
|
||||
assertThat(existingFile.lastModified(), is(expectedLastModified));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedForNonExistingFileThrowsUncheckedIOExceptionWithPathInMessage() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path pathOfNonExistingFile = filesystemPath.resolve("nonExistingFile");
|
||||
File nonExistingFile = NioFileSystem.rootedAt(filesystemPath) //
|
||||
.file("nonExistingFile");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(pathOfNonExistingFile.toString());
|
||||
|
||||
nonExistingFile.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedForNonFileWhichIsAFolderThrowsUncheckedIOExceptionWithPathInMessage() {
|
||||
Path filesystemPath = testFilesystem(folder("nameOfAnExistingFolder"));
|
||||
Path pathOfNonExistingFile = filesystemPath.resolve("nameOfAnExistingFolder");
|
||||
File fileWhichIsAFolder = NioFileSystem.rootedAt(filesystemPath) //
|
||||
.file("nameOfAnExistingFolder");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(pathOfNonExistingFile.toString());
|
||||
|
||||
fileWhichIsAFolder.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareToReturnsZeroForSameInstance() {
|
||||
File file = NioFileSystem.rootedAt(emptyFilesystem()).file("fileName");
|
||||
|
||||
assertThat(file.compareTo(file), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareToReturnsZeroForSameFile() {
|
||||
FileSystem filesystem = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
File fileA = filesystem.file("fileName");
|
||||
File fileB = filesystem.file("fileName");
|
||||
|
||||
assertThat(fileA.compareTo(fileB), is(0));
|
||||
assertThat(fileB.compareTo(fileA), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareToReturnsNonZeroForOtherFile() {
|
||||
FileSystem filesystem = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
File fileA = filesystem.file("aFileName");
|
||||
File fileB = filesystem.file("anotherFileName");
|
||||
|
||||
int compareAWithB = fileA.compareTo(fileB);
|
||||
int compareBWithA = fileB.compareTo(fileA);
|
||||
assertThat(compareAWithB, not(is(0)));
|
||||
assertThat(compareBWithA, not(is(0)));
|
||||
assertThat(signum(compareAWithB) + signum(compareBWithA), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareToThrowsExceptionForFileFromDifferentFileSystem() {
|
||||
File fileA = NioFileSystem.rootedAt(emptyFilesystem()).file("aFileName");
|
||||
File fileB = NioFileSystem.rootedAt(emptyFilesystem()).file("aFileName");
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
|
||||
fileA.compareTo(fileB);
|
||||
assertThat(name, is(fileName.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path absoluteFilePath = filesystemPath.resolve("fileName").toAbsolutePath();
|
||||
File file = NioFileSystem.rootedAt(filesystemPath).file("fileName");
|
||||
|
||||
assertThat(file.toString(), is(format("NioFile(%s)", absoluteFilePath)));
|
||||
assertThat(inTest.toString(), is(format("NioFile(%s)", path)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNameReturnsNameOfFile() {
|
||||
String fileName = "fileName";
|
||||
File file = NioFileSystem.rootedAt(emptyFilesystem()).file(fileName);
|
||||
|
||||
assertThat(file.name(), is(fileName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentForDirectChildOfFileSystemReturnsFileSystem() {
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
File file = fileSystem.file("fileName");
|
||||
|
||||
assertThat(file.parent(), presentOptionalWithValueThat(is(sameInstance(fileSystem))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentForChildOfFolderReturnsFolder() {
|
||||
Folder folder = NioFileSystem.rootedAt(emptyFilesystem()).folder("folderName");
|
||||
File file = folder.file("fileName");
|
||||
|
||||
assertThat(file.parent(), presentOptionalWithValueThat(is(sameInstance(folder))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToNonExistingTargetCreatesTargetWithContent() {
|
||||
Path filesystemPath = testFilesystem(file("sourceFile").withData("fileContents"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path sourceFilePath = filesystemPath.resolve("sourceFile");
|
||||
Path targetFilePath = filesystemPath.resolve("targetFile");
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("targetFile");
|
||||
|
||||
source.copyTo(target);
|
||||
|
||||
assertThat(sourceFilePath, isFile().withContent("fileContents"));
|
||||
assertThat(targetFilePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToExistingTargetOverwritesTargetWithContent() {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file("sourceFile").withData("fileContents"), //
|
||||
file("targetFile").withData("wrongFileContents"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path sourceFilePath = filesystemPath.resolve("sourceFile");
|
||||
Path targetFilePath = filesystemPath.resolve("targetFile");
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("targetFile");
|
||||
|
||||
source.copyTo(target);
|
||||
|
||||
assertThat(sourceFilePath, isFile().withContent("fileContents"));
|
||||
assertThat(targetFilePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToSameFileThrowsIllegalArgumentException() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("sourceFile"))).file("fileName");
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
|
||||
file.copyTo(file);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToDirectoryTargetThrowsUncheckedIOExceptionWithPathInMessage() {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file("sourceFile").withData("fileContents"), //
|
||||
folder("aFolderName"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path targetFilePath = filesystemPath.resolve("aFolderName").toAbsolutePath();
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("aFolderName");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(targetFilePath.toAbsolutePath().toString());
|
||||
|
||||
source.copyTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToOfNonExistingFileThrowsUncheckedIOExceptionWithPathInMessage() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path filePath = filesystemPath.resolve("nonExistingFile").toAbsolutePath();
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
File nonExistingFile = fileSystem.file("nonExistingFile");
|
||||
File target = fileSystem.file("target");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filePath.toString());
|
||||
|
||||
nonExistingFile.copyTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToOfFileWhichIsAFolderThrowsUncheckedIOExceptionWithPathInMessage() {
|
||||
Path filesystemPath = testFilesystem(folder("folderName"));
|
||||
Path filePath = filesystemPath.resolve("folderName").toAbsolutePath();
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
File fileWhichIsAFolder = fileSystem.file("folderName");
|
||||
File target = fileSystem.file("target");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filePath.toString());
|
||||
|
||||
fileWhichIsAFolder.copyTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToNonExistingTargetCreatesTargetWithContentAndDeletesSource() {
|
||||
Path filesystemPath = testFilesystem(file("sourceFile").withData("fileContents"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path sourceFilePath = filesystemPath.resolve("sourceFile");
|
||||
Path targetFilePath = filesystemPath.resolve("targetFile");
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("targetFile");
|
||||
|
||||
source.moveTo(target);
|
||||
|
||||
assertThat(sourceFilePath, doesNotExist());
|
||||
assertThat(targetFilePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToExistingTargetOverwritesTargetWithContentAndDeletesSource() {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file("sourceFile").withData("fileContents"), //
|
||||
file("targetFile").withData("wrongFileContents"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path sourceFilePath = filesystemPath.resolve("sourceFile");
|
||||
Path targetFilePath = filesystemPath.resolve("targetFile");
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("targetFile");
|
||||
|
||||
source.moveTo(target);
|
||||
|
||||
assertThat(sourceFilePath, doesNotExist());
|
||||
assertThat(targetFilePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToSameFileDoesNothing() {
|
||||
Path filesystemPath = testFilesystem(file("fileName").withData("fileContents"));
|
||||
Path filePath = filesystemPath.resolve("fileName");
|
||||
File file = NioFileSystem.rootedAt(filesystemPath).file("fileName");
|
||||
|
||||
file.moveTo(file);
|
||||
|
||||
assertThat(filePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToDirectoryTargetThrowsUncheckedIOExceptionWithPathInMessage() {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file("sourceFile").withData("fileContents"), //
|
||||
folder("aFolderName"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path targetFilePath = filesystemPath.resolve("aFolderName").toAbsolutePath();
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("aFolderName");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(targetFilePath.toAbsolutePath().toString());
|
||||
|
||||
source.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfNonExistingFileThrowsUncheckedIOExceptionWithPathInMessage() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path filePath = filesystemPath.resolve("nonExistingFile").toAbsolutePath();
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
File nonExistingFile = fileSystem.file("nonExistingFile");
|
||||
File target = fileSystem.file("target");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filePath.toString());
|
||||
|
||||
nonExistingFile.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFileWhichIsAFolderThrowsUncheckedIOExceptionWithPathInMessage() {
|
||||
Path filesystemPath = testFilesystem(folder("folderName"));
|
||||
Path filePath = filesystemPath.resolve("folderName").toAbsolutePath();
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
File fileWhichIsAFolder = fileSystem.file("folderName");
|
||||
File target = fileSystem.file("target");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filePath.toString());
|
||||
|
||||
fileWhichIsAFolder.moveTo(target);
|
||||
}
|
||||
|
||||
public class OpenReadable {
|
||||
|
||||
@Test
|
||||
public void testOpenReadableReturnsAReadableNioFileForAnExistingFile() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
|
||||
ReadableFile result = file.openReadable();
|
||||
|
||||
assertThat(result, is(instanceOf(ReadableNioFile.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableThrowsAnUncheckedIOExceptionIfTheFileDoesNotExists() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path nonExistingFilePath = filesystemPath.resolve("nonExistingFile");
|
||||
File nonExistingFile = NioFileSystem.rootedAt(filesystemPath).file("nonExistingFile");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(nonExistingFilePath.toString());
|
||||
|
||||
nonExistingFile.openReadable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableThrowsIllegalStateExceptionIfInvokedTwiceFromWithingTheSameThread() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openReadable();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("Current thread is already reading this file");
|
||||
|
||||
file.openReadable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableDoesNotThrowIllegalStateExceptionIfInvokedTwiceFromWithingTheSameThreadButTheFirstReadableFileHasBeenClosed() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openReadable().close();
|
||||
|
||||
ReadableFile result = file.openReadable();
|
||||
|
||||
assertThat(result, is(instanceOf(ReadableNioFile.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableThrowsIllegalStateExceptionIfInvokedAfterOpenWritable() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openWritable();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("Current thread is currently writing this file");
|
||||
|
||||
file.openReadable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableDoesNotThrowIllegalStateExceptionIfInvokedAfterOpenWritableButTheWritableFileHasBeenClosed() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openWritable().close();
|
||||
|
||||
ReadableFile result = file.openReadable();
|
||||
|
||||
assertThat(result, is(instanceOf(ReadableNioFile.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class OpenWritable {
|
||||
|
||||
@Test
|
||||
public void testOpenWritableReturnsAWritableNioFileForAnExistingFile() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
|
||||
WritableFile result = file.openWritable();
|
||||
|
||||
assertThat(result, is(instanceOf(WritableNioFile.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableReturnsAWritableNioFileForANonExisitingFile() {
|
||||
File file = NioFileSystem.rootedAt(emptyFilesystem()).file("nonExistingFile");
|
||||
|
||||
WritableFile result = file.openWritable();
|
||||
|
||||
assertThat(result, is(instanceOf(WritableNioFile.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableDoesNotCreateANonExisitingFile() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path filePath = filesystemPath.resolve("nonExistingFile");
|
||||
File file = NioFileSystem.rootedAt(filesystemPath).file("nonExistingFile");
|
||||
|
||||
file.openWritable();
|
||||
|
||||
assertThat(filePath, doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableThrowsIllegalStateExceptionIfInvokedTwiceFromWithingTheSameThread() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openWritable();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("Current thread is already writing this file");
|
||||
|
||||
file.openWritable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableDoesNotThrowIllegalStateExceptionIfInvokedTwiceFromWithingTheSameThreadButTheFirstWritableFileHasBeenClosed() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openWritable().close();
|
||||
|
||||
WritableFile result = file.openWritable();
|
||||
|
||||
assertThat(result, is(instanceOf(WritableNioFile.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableThrowsIllegalStateExceptionIfInvokedAfterOpenReadable() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openReadable();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("Current thread is currently reading this file");
|
||||
|
||||
file.openWritable();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableDoesNotThrowIllegalStateExceptionIfInvokedAfterOpenReadableButTheReadableFileHasBeenClosed() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openReadable().close();
|
||||
|
||||
WritableFile result = file.openWritable();
|
||||
|
||||
assertThat(result, is(instanceOf(WritableNioFile.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class OpenWithMultipleThreads {
|
||||
|
||||
@Rule
|
||||
public Timeout timeout = Timeout.seconds(5);
|
||||
|
||||
@Test
|
||||
public void testOpenReadableInvokedInTwoThreadsCompletes() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
|
||||
ReadableFile readableFileFromThread1 = computeInThread(file::openReadable);
|
||||
ReadableFile readableFileFromThread2 = computeInThread(file::openReadable);
|
||||
|
||||
assertThat(readableFileFromThread1, is(instanceOf(ReadableNioFile.class)));
|
||||
assertThat(readableFileFromThread2, is(instanceOf(ReadableNioFile.class)));
|
||||
assertThat(readableFileFromThread1, is(not(sameInstance(readableFileFromThread2))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableInvokedWhileWritableFileIsOpenBlocks() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openWritable();
|
||||
|
||||
Thread thread = inThread(file::openReadable);
|
||||
|
||||
assertThat(thread, is(stackContains(NioFile.class, "openReadable")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableInvokedWhileReadableFileIsOpenBlocks() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openReadable();
|
||||
|
||||
Thread thread = inThread(file::openWritable);
|
||||
|
||||
assertThat(thread, is(stackContains(NioFile.class, "openWritable")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableInvokedWhileWritableFileIsOpenBlocks() {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
file.openWritable();
|
||||
|
||||
Thread thread = inThread(file::openWritable);
|
||||
|
||||
assertThat(thread, is(stackContains(NioFile.class, "openWritable")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenReadableInvokedWhileWritableFileIsOpenCompletesAfterClosingIt() throws InterruptedException {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
WritableFile writableFile = file.openWritable();
|
||||
Thread thread = inThread(file::openReadable);
|
||||
writableFile.close();
|
||||
|
||||
thread.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableInvokedWhileReadableFileIsOpenCompletesAfterClosingIt() throws InterruptedException {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
ReadableFile readableFile = file.openReadable();
|
||||
Thread thread = inThread(file::openWritable);
|
||||
readableFile.close();
|
||||
|
||||
thread.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableInvokedWhileWritableFileIsOpenCompletesAfterClosingIt() throws InterruptedException {
|
||||
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
|
||||
WritableFile writableFile = file.openWritable();
|
||||
Thread thread = inThread(file::openWritable);
|
||||
writableFile.close();
|
||||
|
||||
thread.join();
|
||||
}
|
||||
|
||||
private Thread inThread(Runnable task) {
|
||||
Thread thread = new Thread(task);
|
||||
thread.start();
|
||||
try {
|
||||
// give thread time to execute work
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
private <T> T computeInThread(Supplier<T> computation) {
|
||||
CompletableFuture<T> future = new CompletableFuture<>();
|
||||
inThread(() -> {
|
||||
future.complete(computation.get());
|
||||
});
|
||||
try {
|
||||
return future.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int signum(int value) {
|
||||
if (value > 0) {
|
||||
return 1;
|
||||
} else if (value < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+438
-419
@@ -2,479 +2,498 @@ package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.cryptomator.common.test.matcher.ContainsMatcher.containsInAnyOrder;
|
||||
import static org.cryptomator.common.test.matcher.OptionalMatcher.presentOptionalWithValueThat;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.emptyFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.file;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.folder;
|
||||
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.testFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.NioNodeMatcher.fileWithName;
|
||||
import static org.cryptomator.filesystem.nio.NioNodeMatcher.folderWithName;
|
||||
import static org.cryptomator.filesystem.nio.PathMatcher.doesNotExist;
|
||||
import static org.cryptomator.filesystem.nio.PathMatcher.isDirectory;
|
||||
import static org.cryptomator.filesystem.nio.PathMatcher.isFile;
|
||||
import static org.cryptomator.common.test.matcher.ContainsMatcher.contains;
|
||||
import static org.cryptomator.filesystem.nio.ReflectiveClassMatchers.aClassThatDoesDeclareMethod;
|
||||
import static org.cryptomator.filesystem.nio.ReflectiveClassMatchers.aClassThatDoesNotDeclareMethod;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.CoreMatchers.theInstance;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import de.bechte.junit.runners.context.HierarchicalContextRunner;
|
||||
|
||||
@RunWith(HierarchicalContextRunner.class)
|
||||
public class NioFolderTest {
|
||||
|
||||
private static final String SEPARATOR = "/";
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testNameIsNameOfFolder() throws IOException {
|
||||
final String folderName = "nameOfFolder";
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
Folder folder = fileSystem.folder(folderName);
|
||||
private NioFileSystem fileSystem;
|
||||
private Optional<NioFolder> parent;
|
||||
|
||||
assertThat(folder, folderWithName(folderName));
|
||||
private Path path;
|
||||
private NioAccess nioAccess;
|
||||
private InstanceFactory instanceFactory;
|
||||
|
||||
private NioFolder inTest;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
path = mock(Path.class);
|
||||
nioAccess = mock(NioAccess.class);
|
||||
instanceFactory = mock(InstanceFactory.class);
|
||||
fileSystem = mock(NioFileSystem.class);
|
||||
parent = Optional.of(fileSystem);
|
||||
|
||||
Path maybeNonAbsolutePath = mock(Path.class);
|
||||
when(maybeNonAbsolutePath.toAbsolutePath()).thenReturn(path);
|
||||
|
||||
when(parent.get().fileSystem()).thenReturn(fileSystem);
|
||||
|
||||
when(nioAccess.separator()).thenReturn(SEPARATOR);
|
||||
|
||||
inTest = new NioFolder(parent, maybeNonAbsolutePath, nioAccess, instanceFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSucceedsIfFolderExists() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path testFilesystemPath = testFilesystem( //
|
||||
folder(folderName));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath);
|
||||
Folder existingFolder = fileSystem.folder(folderName);
|
||||
public class ChildrenTests {
|
||||
|
||||
existingFolder.create();
|
||||
@Test
|
||||
public void testChildrenConvertsPathWhichIsADirectoryToAnNioFolderUsingTheInstanceFactory() throws IOException {
|
||||
Path childFolderPath = mock(Path.class);
|
||||
NioFolder nioFolderCreatedFromChildFolderPath = mock(NioFolder.class);
|
||||
Stream<Path> childrenPaths = Stream.<Path>builder().add(childFolderPath).build();
|
||||
when(nioAccess.isDirectory(childFolderPath)).thenReturn(true);
|
||||
when(nioAccess.list(path)).thenReturn(childrenPaths);
|
||||
when(instanceFactory.nioFolder(Optional.of(inTest), childFolderPath, nioAccess)).thenReturn(nioFolderCreatedFromChildFolderPath);
|
||||
|
||||
assertThat(inTest.children().collect(toList()), contains(theInstance(nioFolderCreatedFromChildFolderPath)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenConvertsPathWhichIsAFileToAnNioFileUsingTheInstanceFactory() throws IOException {
|
||||
Path childFilePath = mock(Path.class);
|
||||
NioFile nioFileCreatedFromChildFolderPath = mock(NioFile.class);
|
||||
Stream<Path> childrenPaths = Stream.<Path>builder().add(childFilePath).build();
|
||||
when(nioAccess.isDirectory(childFilePath)).thenReturn(false);
|
||||
when(nioAccess.list(path)).thenReturn(childrenPaths);
|
||||
when(instanceFactory.nioFile(Optional.of(inTest), childFilePath, nioAccess)).thenReturn(nioFileCreatedFromChildFolderPath);
|
||||
|
||||
assertThat(inTest.children().collect(toList()), contains(theInstance(nioFileCreatedFromChildFolderPath)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenConvertsAllPathsToFilesAndFolders() throws IOException {
|
||||
Path childFilePath = mock(Path.class);
|
||||
Path childFolderPath = mock(Path.class);
|
||||
Path anotherChildFilePath = mock(Path.class);
|
||||
Path anotherChildFolderPath = mock(Path.class);
|
||||
NioFile nioFileCreatedFromChildFilePath = mock(NioFile.class);
|
||||
NioFile anotherNioFileCreatedFromChildFilePath = mock(NioFile.class);
|
||||
NioFolder nioFolderCreatedFromChildFolderPath = mock(NioFolder.class);
|
||||
NioFolder anotherNioFolderCreatedFromChildFolderPath = mock(NioFolder.class);
|
||||
Stream<Path> childrenPaths = Stream.<Path>builder() //
|
||||
.add(childFilePath) // NioFolder
|
||||
.add(childFolderPath) //
|
||||
.add(anotherChildFilePath) //
|
||||
.add(anotherChildFolderPath).build();
|
||||
when(nioAccess.isDirectory(childFilePath)).thenReturn(false);
|
||||
when(nioAccess.isDirectory(childFolderPath)).thenReturn(true);
|
||||
when(nioAccess.isDirectory(anotherChildFilePath)).thenReturn(false);
|
||||
when(nioAccess.isDirectory(anotherChildFolderPath)).thenReturn(true);
|
||||
when(nioAccess.list(path)).thenReturn(childrenPaths);
|
||||
when(instanceFactory.nioFile(Optional.of(inTest), childFilePath, nioAccess)).thenReturn(nioFileCreatedFromChildFilePath);
|
||||
when(instanceFactory.nioFolder(Optional.of(inTest), childFolderPath, nioAccess)).thenReturn(nioFolderCreatedFromChildFolderPath);
|
||||
when(instanceFactory.nioFile(Optional.of(inTest), anotherChildFilePath, nioAccess)).thenReturn(anotherNioFileCreatedFromChildFilePath);
|
||||
when(instanceFactory.nioFolder(Optional.of(inTest), anotherChildFolderPath, nioAccess)).thenReturn(anotherNioFolderCreatedFromChildFolderPath);
|
||||
|
||||
assertThat(inTest.children().collect(toList()),
|
||||
contains( //
|
||||
theInstance(nioFileCreatedFromChildFilePath), //
|
||||
theInstance(nioFolderCreatedFromChildFolderPath), //
|
||||
theInstance(anotherNioFileCreatedFromChildFilePath), //
|
||||
theInstance(anotherNioFolderCreatedFromChildFolderPath)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilesIsNotOverwritten() {
|
||||
assertThat(Folder.class, aClassThatDoesDeclareMethod("files"));
|
||||
assertThat(NioFolder.class, aClassThatDoesNotDeclareMethod("files"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFoldersIsNotOverwritten() {
|
||||
assertThat(Folder.class, aClassThatDoesDeclareMethod("folders"));
|
||||
assertThat(NioFolder.class, aClassThatDoesNotDeclareMethod("folders"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenWrapsIOExceptionFromListInUncheckedIOException() throws IOException {
|
||||
IOException exceptionFromList = new IOException();
|
||||
when(nioAccess.list(path)).thenThrow(exceptionFromList);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromList));
|
||||
|
||||
inTest.children();
|
||||
}
|
||||
|
||||
assertThat(Files.isDirectory(testFilesystemPath.resolve(folderName)), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSucceedsIfFolderDoesNotExist() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path testFilesystemPath = emptyFilesystem();
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath);
|
||||
Folder nonExistingFolder = fileSystem.folder(folderName);
|
||||
public class FileTests {
|
||||
|
||||
nonExistingFolder.create();
|
||||
@Test
|
||||
public void testFileResolvesTheNameAgainstThePathAndCreatesAnNioFileUsingTheInstanceFactory() {
|
||||
String name = "theFileName";
|
||||
Path resolvedPath = mock(Path.class);
|
||||
NioFile fileCreatedByInstanceFactory = mock(NioFile.class);
|
||||
when(path.resolve(name)).thenReturn(resolvedPath);
|
||||
when(instanceFactory.nioFile(Optional.of(inTest), resolvedPath, nioAccess)).thenReturn(fileCreatedByInstanceFactory);
|
||||
|
||||
File result = inTest.file(name);
|
||||
|
||||
assertThat(result, is(fileCreatedByInstanceFactory));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondInvocationOfFileReturnsChachedResult() {
|
||||
String name = "theFileName";
|
||||
Path resolvedPath = mock(Path.class);
|
||||
NioFile fileCreatedByInstanceFactory = mock(NioFile.class);
|
||||
when(path.resolve(name)).thenReturn(resolvedPath);
|
||||
when(instanceFactory.nioFile(Optional.of(inTest), resolvedPath, nioAccess)).thenReturn(fileCreatedByInstanceFactory);
|
||||
|
||||
File result = inTest.file(name);
|
||||
File secondResult = inTest.file(name);
|
||||
|
||||
assertThat(result, is(fileCreatedByInstanceFactory));
|
||||
assertThat(secondResult, is(fileCreatedByInstanceFactory));
|
||||
verify(instanceFactory).nioFile(Optional.of(inTest), resolvedPath, nioAccess);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileInvokedWithNameContainingSeparatorThrowsIllegalArgumentException() {
|
||||
String name = "nameContaining" + SEPARATOR;
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage(name);
|
||||
|
||||
inTest.file(name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveFileIsNotOverwritten() {
|
||||
assertThat(Folder.class, is(aClassThatDoesDeclareMethod("resolveFile", String.class)));
|
||||
assertThat(NioFolder.class, is(aClassThatDoesNotDeclareMethod("resolveFile", String.class)));
|
||||
}
|
||||
|
||||
assertThat(Files.isDirectory(testFilesystemPath.resolve(folderName)), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSucceedsIfParentIsMissing() throws IOException {
|
||||
String parentFolderName = "nameOfParentFolder";
|
||||
String folderName = "nameOfFolder";
|
||||
Path emptyFilesystemPath = emptyFilesystem();
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystemPath);
|
||||
Folder folderWithNonExistingParent = fileSystem.folder(parentFolderName).folder(folderName);
|
||||
public class FolderTests {
|
||||
|
||||
folderWithNonExistingParent.create();
|
||||
@Test
|
||||
public void testFolderResolvesTheNameAgainstThePathAndCreatesAnNioFolderUsingTheInstanceFactory() {
|
||||
String name = "theFolderName";
|
||||
Path resolvedPath = mock(Path.class);
|
||||
NioFolder folderCreatedByInstanceFactory = mock(NioFolder.class);
|
||||
when(path.resolve(name)).thenReturn(resolvedPath);
|
||||
when(instanceFactory.nioFolder(Optional.of(inTest), resolvedPath, nioAccess)).thenReturn(folderCreatedByInstanceFactory);
|
||||
|
||||
Folder result = inTest.folder(name);
|
||||
|
||||
assertThat(result, is(folderCreatedByInstanceFactory));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondInvocationOfFileReturnsChachedResult() {
|
||||
String name = "theFolderName";
|
||||
Path resolvedPath = mock(Path.class);
|
||||
NioFolder folderCreatedByInstanceFactory = mock(NioFolder.class);
|
||||
when(path.resolve(name)).thenReturn(resolvedPath);
|
||||
when(instanceFactory.nioFolder(Optional.of(inTest), resolvedPath, nioAccess)).thenReturn(folderCreatedByInstanceFactory);
|
||||
|
||||
Folder result = inTest.folder(name);
|
||||
Folder secondResult = inTest.folder(name);
|
||||
|
||||
assertThat(result, is(folderCreatedByInstanceFactory));
|
||||
assertThat(secondResult, is(folderCreatedByInstanceFactory));
|
||||
verify(instanceFactory).nioFolder(Optional.of(inTest), resolvedPath, nioAccess);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFolderInvokedWithNameContainingSeparatorThrowsIllegalArgumentException() {
|
||||
String name = "nameContaining" + SEPARATOR;
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage(name);
|
||||
|
||||
inTest.folder(name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveFolderIsNotOverwritten() {
|
||||
assertThat(Folder.class, is(aClassThatDoesDeclareMethod("resolveFolder", String.class)));
|
||||
assertThat(NioFolder.class, is(aClassThatDoesNotDeclareMethod("resolveFolder", String.class)));
|
||||
}
|
||||
|
||||
assertThat(Files.isDirectory(emptyFilesystemPath.resolve(parentFolderName).resolve(folderName)), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithFolderWhichIsAFileThrowsUncheckedIOExceptionWithAbsolutePathOfFolderInMessage() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path testFilesystemPath = testFilesystem(file(folderName));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath);
|
||||
Folder folderWhichIsAFile = fileSystem.folder(folderName);
|
||||
public class CreateTests {
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(testFilesystemPath.resolve(folderName).toString());
|
||||
@Test
|
||||
public void testCreateDelegatesToNioAccessCreateDirectories() throws IOException {
|
||||
inTest.create();
|
||||
|
||||
verify(nioAccess).createDirectories(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapesIOExceptionThrownByCreateDirectoriesInUncheckedIOException() throws IOException {
|
||||
IOException exceptionFromCreateDirectories = new IOException();
|
||||
doThrow(exceptionFromCreateDirectories).when(nioAccess).createDirectories(path);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromCreateDirectories));
|
||||
|
||||
inTest.create();
|
||||
}
|
||||
|
||||
folderWhichIsAFile.create();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenOfEmptyNioFolderAreEmpty() throws IOException {
|
||||
NioFolder folder = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
public class LastModifiedTests {
|
||||
|
||||
@Test
|
||||
public void testLastModifiedReturnsLastModifiedTimeForExistingFolder() throws IOException {
|
||||
Instant instant = Instant.parse("2016-01-04T01:24:32Z");
|
||||
when(nioAccess.exists(path)).thenReturn(true);
|
||||
when(nioAccess.isDirectory(path)).thenReturn(true);
|
||||
when(nioAccess.getLastModifiedTime(path)).thenReturn(FileTime.from(instant));
|
||||
|
||||
Instant result = inTest.lastModified();
|
||||
|
||||
assertThat(result, is(instant));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedInvokesGetLastModifiedTimeForNonExistingFolder() throws IOException {
|
||||
Instant instant = Instant.parse("2016-01-04T01:24:32Z");
|
||||
when(nioAccess.exists(path)).thenReturn(false);
|
||||
when(nioAccess.getLastModifiedTime(path)).thenReturn(FileTime.from(instant));
|
||||
|
||||
Instant result = inTest.lastModified();
|
||||
|
||||
assertThat(result, is(instant));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedWrapsIOExceptionThrownByGetLastModifiedTimeInUncheckedIOException() throws IOException {
|
||||
IOException exceptionThrownFromGetLastModifiedTime = new IOException();
|
||||
when(nioAccess.exists(path)).thenReturn(true);
|
||||
when(nioAccess.isDirectory(path)).thenReturn(true);
|
||||
when(nioAccess.getLastModifiedTime(path)).thenThrow(exceptionThrownFromGetLastModifiedTime);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionThrownFromGetLastModifiedTime));
|
||||
|
||||
inTest.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedThrowsUncheckedIOExceptionIfPathExistsButIsNoFolder() {
|
||||
when(nioAccess.exists(path)).thenReturn(true);
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(format("%s is a file", path));
|
||||
|
||||
inTest.lastModified();
|
||||
}
|
||||
|
||||
assertThat(folder.children().collect(toList()), is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenOfNonExistingFolderThrowsUncheckedIOExceptionWithAbolutePathOfFolderInMessage() throws IOException {
|
||||
String nameOfNonExistingFolder = "nameOfFolder";
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(nameOfNonExistingFolder);
|
||||
public class ExistTests {
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve(nameOfNonExistingFolder).toString());
|
||||
@Test
|
||||
public void testExistsIfExisting() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(true);
|
||||
|
||||
assertThat(inTest.exists(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsIfNotExisting() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
|
||||
assertThat(inTest.exists(), is(false));
|
||||
}
|
||||
|
||||
folder.children();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenOfFolderWhichIsAFileThrowsUncheckedIOExceptionWithAbsolutePathOfFolderInMessage() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem(file(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
public class MoveToTests {
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve(folderName).toString());
|
||||
@Test
|
||||
public void testMoveToThrowsIllegalArgumentExceptionIfTargetDoesNotBelongToSameFilesystem() {
|
||||
Folder target = mock(Folder.class);
|
||||
when(target.fileSystem()).thenReturn(mock(FileSystem.class));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Can only move a Folder to a Folder in the same FileSystem");
|
||||
|
||||
inTest.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToWrapsIOExceptionThrownByNioAccessMoveInUncheckedIOException() throws IOException {
|
||||
NioFolder target = mock(NioFolder.class);
|
||||
Path targetPath = mock(Path.class);
|
||||
when(target.fileSystem()).thenReturn(fileSystem);
|
||||
when(target.path()).thenReturn(targetPath);
|
||||
when(target.parent()).thenReturn(Optional.empty());
|
||||
IOException exceptionThrownByMoveTo = new IOException();
|
||||
doThrow(exceptionThrownByMoveTo).when(nioAccess).move(path, targetPath);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionThrownByMoveTo));
|
||||
|
||||
inTest.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToDeletesTargetAndDelegatesToNioAccessMoveIfTargetHasNoParent() throws IOException {
|
||||
NioFolder target = mock(NioFolder.class);
|
||||
Path targetPath = mock(Path.class);
|
||||
when(target.fileSystem()).thenReturn(fileSystem);
|
||||
when(target.path()).thenReturn(targetPath);
|
||||
when(target.parent()).thenReturn(Optional.empty());
|
||||
|
||||
inTest.moveTo(target);
|
||||
|
||||
InOrder inOrder = inOrder(target, nioAccess);
|
||||
inOrder.verify(target).delete();
|
||||
inOrder.verify(nioAccess).move(path, targetPath);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Test
|
||||
public void testMoveToDeletesTargetCreatesTargetsParentAndDelegatesToNioAccessMoveIfTargetHasAParent() throws IOException {
|
||||
NioFolder target = mock(NioFolder.class);
|
||||
NioFolder parentOfTarget = mock(NioFolder.class);
|
||||
Path targetPath = mock(Path.class);
|
||||
when(target.fileSystem()).thenReturn(fileSystem);
|
||||
when(target.path()).thenReturn(targetPath);
|
||||
when(target.parent()).thenReturn((Optional) Optional.of(parentOfTarget));
|
||||
|
||||
inTest.moveTo(target);
|
||||
|
||||
InOrder inOrder = inOrder(target, parentOfTarget, nioAccess);
|
||||
inOrder.verify(target).delete();
|
||||
inOrder.verify(parentOfTarget).create();
|
||||
inOrder.verify(nioAccess).move(path, targetPath);
|
||||
}
|
||||
|
||||
folder.children();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenOfFolderAreCorrect() throws IOException {
|
||||
String folderName1 = "folder1";
|
||||
String folderName2 = "folder2";
|
||||
String fileName1 = "file1";
|
||||
String fileName2 = "file2";
|
||||
public class DeleteTests {
|
||||
|
||||
@Test
|
||||
public void testDeleteDoesNothingIfFolderDoesNotExist() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
|
||||
inTest.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteInvokesDeleteOnChildFolderAndNioAccessDeleteAfterwards() throws IOException {
|
||||
Path folderChildPath = mock(Path.class);
|
||||
NioFolder folderChild = mock(NioFolder.class);
|
||||
when(nioAccess.isDirectory(path)).thenReturn(true);
|
||||
when(nioAccess.isDirectory(folderChildPath)).thenReturn(true);
|
||||
when(instanceFactory.nioFolder(Optional.of(inTest), folderChildPath, nioAccess)).thenReturn(folderChild);
|
||||
when(nioAccess.list(path)).thenAnswer(answerFrom(() -> Stream.of(folderChildPath)));
|
||||
|
||||
inTest.delete();
|
||||
|
||||
InOrder inOrder = inOrder(nioAccess, folderChild);
|
||||
inOrder.verify(nioAccess).isDirectory(path);
|
||||
inOrder.verify(nioAccess).list(path);
|
||||
inOrder.verify(folderChild).delete();
|
||||
inOrder.verify(nioAccess).list(path);
|
||||
inOrder.verify(nioAccess).delete(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteInvokesDeleteOnChildFileAndNioAccessDeleteAfterwards() throws IOException {
|
||||
Path fileChildPath = mock(Path.class);
|
||||
NioFile fileChild = mock(NioFile.class);
|
||||
WritableFile writableFile = mock(WritableFile.class);
|
||||
when(fileChild.openWritable()).thenReturn(writableFile);
|
||||
when(nioAccess.isDirectory(path)).thenReturn(true);
|
||||
when(nioAccess.isDirectory(fileChildPath)).thenReturn(false);
|
||||
when(instanceFactory.nioFile(Optional.of(inTest), fileChildPath, nioAccess)).thenReturn(fileChild);
|
||||
when(nioAccess.list(path)).thenAnswer(answerFrom(() -> Stream.of(fileChildPath)));
|
||||
|
||||
inTest.delete();
|
||||
|
||||
InOrder inOrder = inOrder(nioAccess, fileChild, writableFile);
|
||||
inOrder.verify(nioAccess).isDirectory(path);
|
||||
inOrder.verify(nioAccess).list(path);
|
||||
inOrder.verify(nioAccess).list(path);
|
||||
inOrder.verify(fileChild).openWritable();
|
||||
inOrder.verify(writableFile).delete();
|
||||
inOrder.verify(writableFile).close();
|
||||
inOrder.verify(nioAccess).delete(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWrapsIOExceptionFromFolderDeleteInUncheckedIOException() throws IOException {
|
||||
Path fileChildPath = mock(Path.class);
|
||||
NioFile fileChild = mock(NioFile.class);
|
||||
WritableFile writableFile = mock(WritableFile.class);
|
||||
when(fileChild.openWritable()).thenReturn(writableFile);
|
||||
when(nioAccess.isDirectory(path)).thenReturn(true);
|
||||
when(nioAccess.isDirectory(fileChildPath)).thenReturn(false);
|
||||
when(instanceFactory.nioFile(Optional.of(inTest), fileChildPath, nioAccess)).thenReturn(fileChild);
|
||||
when(nioAccess.list(path)).thenAnswer(answerFrom(() -> Stream.of(fileChildPath)));
|
||||
IOException exceptionFromDelete = new IOException();
|
||||
doThrow(exceptionFromDelete).when(nioAccess).delete(path);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromDelete));
|
||||
|
||||
inTest.delete();
|
||||
}
|
||||
|
||||
NioFolder folder = NioFileSystem.rootedAt(testFilesystem( //
|
||||
folder(folderName1), //
|
||||
folder(folderName2), //
|
||||
file(fileName1), //
|
||||
file(fileName2)));
|
||||
|
||||
assertThat(folder.children().collect(toList()),
|
||||
containsInAnyOrder( //
|
||||
folderWithName(folderName1), //
|
||||
folderWithName(folderName2), //
|
||||
fileWithName(fileName1), //
|
||||
fileWithName(fileName2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilesDoesContainOnlyFileChildren() throws IOException {
|
||||
String folderName1 = "folder1";
|
||||
String folderName2 = "folder2";
|
||||
String fileName1 = "file1";
|
||||
String fileName2 = "file2";
|
||||
|
||||
NioFolder folder = NioFileSystem.rootedAt(testFilesystem( //
|
||||
folder(folderName1), //
|
||||
folder(folderName2), //
|
||||
file(fileName1), //
|
||||
file(fileName2)));
|
||||
|
||||
assertThat(folder.files().collect(toList()),
|
||||
containsInAnyOrder( //
|
||||
fileWithName(fileName1), //
|
||||
fileWithName(fileName2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilesOfNonExistingFolderThrowsUncheckedIOExceptionWithAbolutePathOfFolderInMessage() throws IOException {
|
||||
Path emptyFolderPath = emptyFilesystem();
|
||||
NioFolder folder = NioFileSystem.rootedAt(emptyFolderPath);
|
||||
Files.delete(emptyFolderPath);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(emptyFolderPath.toString());
|
||||
|
||||
folder.files();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFoldersDoesContainOnlyFolderChildren() throws IOException {
|
||||
String folderName1 = "folder1";
|
||||
String folderName2 = "folder2";
|
||||
String fileName1 = "file1";
|
||||
String fileName2 = "file2";
|
||||
|
||||
NioFolder folder = NioFileSystem.rootedAt(testFilesystem( //
|
||||
folder(folderName1), //
|
||||
folder(folderName2), //
|
||||
file(fileName1), //
|
||||
file(fileName2)));
|
||||
|
||||
assertThat(folder.folders().collect(toList()),
|
||||
containsInAnyOrder( //
|
||||
folderWithName(folderName1), //
|
||||
folderWithName(folderName2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFoldersOfNonExistingFolderThrowsUncheckedIOExceptionWithAbolutePathOfFolderInMessage() throws IOException {
|
||||
Path emptyFilesystemPath = emptyFilesystem();
|
||||
NioFolder folder = NioFileSystem.rootedAt(emptyFilesystemPath);
|
||||
Files.delete(emptyFilesystemPath);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(emptyFilesystemPath.toString());
|
||||
|
||||
folder.folders();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOfEmptyFolderDeletesIt() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
folder.delete();
|
||||
|
||||
assertThat(filesystemPath.resolve(folderName), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOfFolderWithChildrenDeletesItAndAllChildrenRecursive() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder(folderName), //
|
||||
folder(folderName + "/subfolder1"), //
|
||||
file(folderName + "/subfolder1/fileName1"), //
|
||||
folder(folderName + "/subfolder2"), //
|
||||
file(folderName + "/fileName1"), //
|
||||
file(folderName + "/fileName2"));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
folder.delete();
|
||||
|
||||
assertThat(filesystemPath.resolve(folderName), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOfNonExistingFolderDoesNothing() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Folder nonExistingFolder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
nonExistingFolder.delete();
|
||||
|
||||
assertThat(filesystemPath.resolve(folderName), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOfFolderWhichIsAFileDoesNothing() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
folder.delete();
|
||||
|
||||
assertThat(filesystemPath.resolve(folderName), isFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsReturnsTrueForExistingDirectory() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
assertThat(folder.exists(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsReturnsFalseForNonExistingDirectory() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Folder nonExistingFolder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
assertThat(nonExistingFolder.exists(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsReturnsFalseForDirectoryWhichIsAFile() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
assertThat(folder.exists(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAncestorOfWithChildReturnsTrue() throws IOException {
|
||||
Folder folder = NioFileSystem.rootedAt(emptyFilesystem()).folder("a");
|
||||
Folder child = folder.folder("b");
|
||||
|
||||
assertThat(folder.isAncestorOf(child), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAncestorOfWithChildOfChildReturnsTrue() throws IOException {
|
||||
Folder folder = NioFileSystem.rootedAt(emptyFilesystem()).folder("a");
|
||||
Folder child = folder.folder("b");
|
||||
File childOfChild = child.file("c");
|
||||
|
||||
assertThat(folder.isAncestorOf(childOfChild), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAncestorOfWithSiblingReturnsFalse() throws IOException {
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
Folder folder = fileSystem.folder("a");
|
||||
Folder sibling = fileSystem.folder("b");
|
||||
|
||||
assertThat(folder.isAncestorOf(sibling), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAncestorOfWithParentReturnsFalse() throws IOException {
|
||||
Folder parent = NioFileSystem.rootedAt(emptyFilesystem()).folder("a");
|
||||
Folder folder = parent.folder("b");
|
||||
|
||||
assertThat(folder.isAncestorOf(parent), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedOfExistingFolderReturnsLastModifiedDate() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Instant lastModified = Instant.parse("2015-12-29T15:36:10.00Z");
|
||||
Folder folder = NioFileSystem
|
||||
.rootedAt(testFilesystem( //
|
||||
folder(folderName).withLastModified(lastModified))) //
|
||||
.folder(folderName);
|
||||
|
||||
assertThat(folder.lastModified(), is(lastModified));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedOfNonExistingFolderThrowsUncheckedIOExceptionWithAbsolutePathOfFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve(folderName).toString());
|
||||
|
||||
folder.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedOfFolderWhichIsAFileThrowsUncheckedIOExceptionWithAbsolutePathOfFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem(file(folderName));
|
||||
Folder folder = NioFileSystem //
|
||||
.rootedAt(filesystemPath) //
|
||||
.folder(folderName);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve(folderName).toString());
|
||||
|
||||
folder.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentOfDirectChildOfFilesystemReturnsFilesystem() throws IOException {
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
Folder folder = fileSystem.folder("aName");
|
||||
|
||||
assertThat(folder.parent(), presentOptionalWithValueThat(is(sameInstance(fileSystem))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentOfChildOfFolderReturnsFolder() throws IOException {
|
||||
Folder folder = NioFileSystem.rootedAt(emptyFilesystem()).folder("aName");
|
||||
Folder child = folder.folder("anotherName");
|
||||
|
||||
assertThat(child.parent(), presentOptionalWithValueThat(is(sameInstance(folder))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderToNonExistingFolderMovesFolder() throws IOException {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder("folderToMove"));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Folder folderToMove = fileSystem.folder("folderToMove");
|
||||
Folder folderToMoveTo = fileSystem.folder("folderToMoveTo");
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
|
||||
assertThat(filesystemPath.resolve("folderToMove"), doesNotExist());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo"), isDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderWithChildrenMovesFolderAndChildren() throws IOException {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder("folderToMove"), //
|
||||
folder("folderToMove/subfolder1"), //
|
||||
folder("folderToMove/subfolder2"), //
|
||||
file("folderToMove/subfolder1/file1").withData("dataOfFile1"), //
|
||||
file("folderToMove/file2").withData("dataOfFile2"), //
|
||||
file("folderToMove/file3").withData("dataOfFile3"));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Folder folderToMove = fileSystem.folder("folderToMove");
|
||||
Folder folderToMoveTo = fileSystem.folder("folderToMoveTo");
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
|
||||
assertThat(filesystemPath.resolve("folderToMove"), doesNotExist());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/subfolder1"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/subfolder2"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/subfolder1/file1"), isFile().withContent("dataOfFile1"));
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/file2"), isFile().withContent("dataOfFile2"));
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/file3"), isFile().withContent("dataOfFile3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderToExistingFolderReplacesTargetFolder() throws IOException {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder("folderToMove"), //
|
||||
file("folderToMove/file1").withData("dataOfFile1"), //
|
||||
file("folderToMove/file2").withData("dataOfFile2"), //
|
||||
folder("folderToMoveTo"), //
|
||||
file("folderToMoveTo/file1").withData("wrongDataOfFile1"), //
|
||||
file("folderToMoveTo/fileWhichShouldNotExistAfterMove"));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Folder folderToMove = fileSystem.folder("folderToMove");
|
||||
Folder folderToMoveTo = fileSystem.folder("folderToMoveTo");
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
|
||||
assertThat(filesystemPath.resolve("folderToMove"), doesNotExist());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/file1"), isFile().withContent("dataOfFile1"));
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/file2"), isFile().withContent("dataOfFile2"));
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/fileWhichShouldNotExistAfterMove"), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderToExistingFileThrowsUncheckedIOExceptionWithAbsolutePathOfTarget() throws IOException {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder("folderToMove"), //
|
||||
file("folderToMoveTo"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Folder folderToMove = fileSystem.folder("folderToMove");
|
||||
Folder folderToMoveTo = fileSystem.folder("folderToMoveTo");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve("folderToMoveTo").toString());
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderToFolderOfAnotherFileSystemDoesNothingAndhrowsIllegalArgumentException() throws IOException {
|
||||
Path filesystemPath = testFilesystem(folder("folderToMove"));
|
||||
Folder folderToMove = NioFileSystem.rootedAt(filesystemPath).folder("folderToMove");
|
||||
Folder folderToMoveTo = NioFileSystem.rootedAt(filesystemPath).folder("folderToMoveTo");
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
|
||||
assertThat(filesystemPath.resolve("folderToMove"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo"), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path pathOfFolder = filesystemPath.resolve("aFolder").toAbsolutePath();
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder("aFolder");
|
||||
|
||||
assertThat(folder.toString(), is(format("NioFolder(%s)", pathOfFolder)));
|
||||
assertThat(inTest.toString(), is(format("NioFolder(%s)", path)));
|
||||
}
|
||||
|
||||
}
|
||||
private <T> Answer<T> answerFrom(Supplier<T> supplier) {
|
||||
return new Answer<T>() {
|
||||
@Override
|
||||
public T answer(InvocationOnMock invocation) throws Throwable {
|
||||
return supplier.get();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+43
-78
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.cryptomator.filesystem.nio.OpenMode.READ;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -13,57 +14,52 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import de.bechte.junit.runners.context.HierarchicalContextRunner;
|
||||
|
||||
@RunWith(HierarchicalContextRunner.class)
|
||||
@SuppressWarnings("resource")
|
||||
public class ReadableNioFileTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private NioFile file;
|
||||
private FileSystem fileSystem;
|
||||
|
||||
private Path path;
|
||||
|
||||
private SharedFileChannel channel;
|
||||
|
||||
private ReadWriteLock lock;
|
||||
private Runnable afterCloseCallback;
|
||||
|
||||
private Lock readLock;
|
||||
private ReadableNioFile inTest;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
file = mock(NioFile.class);
|
||||
fileSystem = mock(FileSystem.class);
|
||||
path = mock(Path.class);
|
||||
channel = mock(SharedFileChannel.class);
|
||||
lock = mock(ReadWriteLock.class);
|
||||
readLock = mock(Lock.class);
|
||||
afterCloseCallback = mock(Runnable.class);
|
||||
|
||||
when(file.channel()).thenReturn(channel);
|
||||
when(file.lock()).thenReturn(lock);
|
||||
when(lock.readLock()).thenReturn(readLock);
|
||||
inTest = new ReadableNioFile(fileSystem, path, channel, afterCloseCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorInvokesOpenWithReadModeOnChannelOfNioFile() {
|
||||
new ReadableNioFile(file);
|
||||
|
||||
verify(channel).open(READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFailsIfClosed() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
ByteBuffer irrelevant = null;
|
||||
inTest.close();
|
||||
|
||||
@@ -75,7 +71,6 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testPositionFailsIfClosed() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
int irrelevant = 1;
|
||||
inTest.close();
|
||||
|
||||
@@ -87,7 +82,6 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testPositionFailsIfNegative() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
|
||||
@@ -96,7 +90,6 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testReadDelegatesToChannelReadFullyWithZeroPositionIfNotSet() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
|
||||
inTest.read(buffer);
|
||||
@@ -106,7 +99,6 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testReadDelegatesToChannelReadFullyWithPositionAtEndOfPreviousReadIfInvokedTwice() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
int endOfPreviousRead = 10;
|
||||
when(channel.readFully(0, buffer)).thenReturn(endOfPreviousRead);
|
||||
@@ -120,7 +112,6 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testReadDelegatesToChannelReadFullyWithPositionUnchangedIfPreviousReadReturnedEof() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
when(channel.readFully(0, buffer)).thenReturn(SharedFileChannel.EOF);
|
||||
|
||||
@@ -132,7 +123,6 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testReadDelegatesToChannelReadFullyWithSetPosition() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
int position = 10;
|
||||
inTest.position(position);
|
||||
@@ -144,7 +134,6 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testReadReturnsValueOfChannelReadFully() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
int expectedResult = 37028;
|
||||
when(channel.readFully(0, buffer)).thenReturn(expectedResult);
|
||||
@@ -156,7 +145,6 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testReadDoesNotModifyBuffer() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
|
||||
inTest.read(buffer);
|
||||
@@ -166,68 +154,56 @@ public class ReadableNioFileTest {
|
||||
|
||||
public class CopyTo {
|
||||
|
||||
@Mock
|
||||
private NioFile otherFile;
|
||||
private WritableNioFile target;
|
||||
|
||||
@Mock
|
||||
private WritableNioFile writableOtherFile;
|
||||
|
||||
@Mock
|
||||
private SharedFileChannel otherChannel;
|
||||
private SharedFileChannel channelOfTarget;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
otherFile = mock(NioFile.class);
|
||||
writableOtherFile = mock(WritableNioFile.class);
|
||||
otherChannel = mock(SharedFileChannel.class);
|
||||
|
||||
when(writableOtherFile.nioFile()).thenReturn(otherFile);
|
||||
when(writableOtherFile.channel()).thenReturn(otherChannel);
|
||||
target = mock(WritableNioFile.class);
|
||||
channelOfTarget = mock(SharedFileChannel.class);
|
||||
when(target.fileSystem()).thenReturn(fileSystem);
|
||||
when(target.channel()).thenReturn(channelOfTarget);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToFailsIfTargetBelongsToOtherFileSystem() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
when(otherFile.belongsToSameFilesystem(file)).thenReturn(false);
|
||||
WritableNioFile targetFromOtherFileSystem = mock(WritableNioFile.class);
|
||||
when(targetFromOtherFileSystem.fileSystem()).thenReturn(mock(FileSystem.class));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
|
||||
inTest.copyTo(writableOtherFile);
|
||||
inTest.copyTo(targetFromOtherFileSystem);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToFailsIfSourceIsClosed() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
when(otherFile.belongsToSameFilesystem(file)).thenReturn(true);
|
||||
when(target.fileSystem()).thenReturn(fileSystem);
|
||||
inTest.close();
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.copyTo(writableOtherFile);
|
||||
inTest.copyTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToAssertsThatTargetIsOpenEnsuresTargetChannelIsOpenTuncatesItAndTransfersDataFromSourceChannel() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
when(otherFile.belongsToSameFilesystem(file)).thenReturn(true);
|
||||
long sizeOfSourceChannel = 3283;
|
||||
when(channel.size()).thenReturn(sizeOfSourceChannel);
|
||||
when(channel.transferTo(0, sizeOfSourceChannel, otherChannel, 0)).thenReturn(sizeOfSourceChannel);
|
||||
when(channel.transferTo(0, sizeOfSourceChannel, channelOfTarget, 0)).thenReturn(sizeOfSourceChannel);
|
||||
|
||||
inTest.copyTo(writableOtherFile);
|
||||
inTest.copyTo(target);
|
||||
|
||||
InOrder inOrder = inOrder(writableOtherFile, otherChannel, channel);
|
||||
inOrder.verify(writableOtherFile).assertOpen();
|
||||
inOrder.verify(writableOtherFile).ensureChannelIsOpened();
|
||||
inOrder.verify(otherChannel).truncate(0);
|
||||
inOrder.verify(channel).transferTo(0, sizeOfSourceChannel, otherChannel, 0);
|
||||
InOrder inOrder = inOrder(target, channel, channelOfTarget);
|
||||
inOrder.verify(target).assertOpen();
|
||||
inOrder.verify(target).ensureChannelIsOpened();
|
||||
inOrder.verify(channelOfTarget).truncate(0);
|
||||
inOrder.verify(channel).transferTo(0, sizeOfSourceChannel, channelOfTarget, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToInvokesTransferToUntilAllBytesHaveBeenTransferred() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
when(otherFile.belongsToSameFilesystem(file)).thenReturn(true);
|
||||
long firstTransferAmount = 100;
|
||||
long secondTransferAmount = 300;
|
||||
long thirdTransferAmount = 500;
|
||||
@@ -235,30 +211,27 @@ public class ReadableNioFileTest {
|
||||
long sizeRemainingAfterFirstTransfer = sizeRemainingAfterSecondTransfer + secondTransferAmount;
|
||||
long size = sizeRemainingAfterFirstTransfer + firstTransferAmount;
|
||||
when(channel.size()).thenReturn(size);
|
||||
when(channel.transferTo(0, size, otherChannel, 0)).thenReturn(firstTransferAmount);
|
||||
when(channel.transferTo(firstTransferAmount, sizeRemainingAfterFirstTransfer, otherChannel, firstTransferAmount)).thenReturn(secondTransferAmount);
|
||||
when(channel.transferTo(firstTransferAmount + secondTransferAmount, sizeRemainingAfterSecondTransfer, otherChannel, firstTransferAmount + secondTransferAmount)).thenReturn(thirdTransferAmount);
|
||||
when(channel.transferTo(0, size, channelOfTarget, 0)).thenReturn(firstTransferAmount);
|
||||
when(channel.transferTo(firstTransferAmount, sizeRemainingAfterFirstTransfer, channelOfTarget, firstTransferAmount)).thenReturn(secondTransferAmount);
|
||||
when(channel.transferTo(firstTransferAmount + secondTransferAmount, sizeRemainingAfterSecondTransfer, channelOfTarget, firstTransferAmount + secondTransferAmount)).thenReturn(thirdTransferAmount);
|
||||
|
||||
inTest.copyTo(writableOtherFile);
|
||||
inTest.copyTo(target);
|
||||
|
||||
InOrder inOrder = inOrder(channel);
|
||||
inOrder.verify(channel).transferTo(0, size, otherChannel, 0);
|
||||
inOrder.verify(channel).transferTo(firstTransferAmount, sizeRemainingAfterFirstTransfer, otherChannel, firstTransferAmount);
|
||||
inOrder.verify(channel).transferTo(firstTransferAmount + secondTransferAmount, sizeRemainingAfterSecondTransfer, otherChannel, firstTransferAmount + secondTransferAmount);
|
||||
inOrder.verify(channel).transferTo(0, size, channelOfTarget, 0);
|
||||
inOrder.verify(channel).transferTo(firstTransferAmount, sizeRemainingAfterFirstTransfer, channelOfTarget, firstTransferAmount);
|
||||
inOrder.verify(channel).transferTo(firstTransferAmount + secondTransferAmount, sizeRemainingAfterSecondTransfer, channelOfTarget, firstTransferAmount + secondTransferAmount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOpenReturnsTrueForNewReadableNioFile() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
|
||||
assertThat(inTest.isOpen(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOpenReturnsFalseForClosed() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
inTest.close();
|
||||
|
||||
assertThat(inTest.isOpen(), is(false));
|
||||
@@ -266,30 +239,25 @@ public class ReadableNioFileTest {
|
||||
|
||||
@Test
|
||||
public void testCloseClosesChannelAndUnlocksReadLock() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
|
||||
inTest.close();
|
||||
|
||||
InOrder inOrder = Mockito.inOrder(channel, readLock);
|
||||
InOrder inOrder = Mockito.inOrder(channel, afterCloseCallback);
|
||||
inOrder.verify(channel).close();
|
||||
inOrder.verify(readLock).unlock();
|
||||
inOrder.verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseClosesChannelAndUnlocksReadLockOnlyOnceIfInvokedTwice() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
|
||||
inTest.close();
|
||||
inTest.close();
|
||||
|
||||
InOrder inOrder = Mockito.inOrder(channel, readLock);
|
||||
InOrder inOrder = Mockito.inOrder(channel, afterCloseCallback);
|
||||
inOrder.verify(channel).close();
|
||||
inOrder.verify(readLock).unlock();
|
||||
inOrder.verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseUnlocksReadLockEvenIfCloseFails() {
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
String message = "exceptionMessage";
|
||||
doThrow(new RuntimeException(message)).when(channel).close();
|
||||
|
||||
@@ -298,16 +266,13 @@ public class ReadableNioFileTest {
|
||||
try {
|
||||
inTest.close();
|
||||
} finally {
|
||||
verify(readLock).unlock();
|
||||
verify(afterCloseCallback).run();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
String nioFileToString = file.toString();
|
||||
ReadableNioFile inTest = new ReadableNioFile(file);
|
||||
|
||||
assertThat(inTest.toString(), is("Readable" + nioFileToString));
|
||||
assertThat(inTest.toString(), is(format("ReadableNioFile(%s)", path)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeDiagnosingMatcher;
|
||||
|
||||
public class ReflectiveClassMatchers {
|
||||
|
||||
public static <T> Matcher<Class<T>> aClassThatDoesNotDeclareMethod(String name, Class<?>... parameterTypes) {
|
||||
return new TypeSafeDiagnosingMatcher<Class<T>>(Class.class) {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description //
|
||||
.appendText(" a Class that does not declare method ") //
|
||||
.appendText(name) //
|
||||
.appendValueList("(", ", ", ")", parameterTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchesSafely(Class<T> item, Description mismatchDescription) {
|
||||
if (declaredMethodToCheck(item)) {
|
||||
mismatchDescription //
|
||||
.appendText(" a Class that declares method ") //
|
||||
.appendText(name) //
|
||||
.appendValueList("(", ", ", ")", parameterTypes);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean declaredMethodToCheck(Class<T> item) {
|
||||
try {
|
||||
return item.getDeclaredMethod(name, parameterTypes) != null;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
} catch (SecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Matcher<Class<T>> aClassThatDoesDeclareMethod(String name, Class<?>... parameterTypes) {
|
||||
return new TypeSafeDiagnosingMatcher<Class<T>>(Class.class) {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description //
|
||||
.appendText(" a Class that does declare method ") //
|
||||
.appendText(name) //
|
||||
.appendValueList("(", ", ", ")", parameterTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchesSafely(Class<T> item, Description mismatchDescription) {
|
||||
if (declaredMethodToCheck(item)) {
|
||||
return true;
|
||||
} else {
|
||||
mismatchDescription //
|
||||
.appendText(" a Class that does not declare method ") //
|
||||
.appendText(name) //
|
||||
.appendValueList("(", ", ", ")", parameterTypes);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean declaredMethodToCheck(Class<T> item) {
|
||||
try {
|
||||
return item.getDeclaredMethod(name, parameterTypes) != null;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
} catch (SecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+587
-192
@@ -1,376 +1,771 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.util.Arrays.copyOf;
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
import static org.apache.commons.io.FileUtils.writeByteArrayToFile;
|
||||
import static org.cryptomator.filesystem.nio.PathMatcher.isFile;
|
||||
import static java.lang.String.format;
|
||||
import static org.cryptomator.filesystem.nio.SharedFileChannel.EOF;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyLong;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
import javax.xml.ws.Holder;
|
||||
|
||||
import org.junit.After;
|
||||
import org.cryptomator.common.Holder;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.rules.Timeout;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import de.bechte.junit.runners.context.HierarchicalContextRunner;
|
||||
|
||||
@RunWith(HierarchicalContextRunner.class)
|
||||
public class SharedFileChannelTest {
|
||||
|
||||
private static final byte[] FILE_CONTENT = "FileContent".getBytes();
|
||||
private static final byte[] OTHER_FILE_CONTENT = "Other".getBytes();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private Path pathOfTestfile;
|
||||
private Path pathOfOtherTestfile;
|
||||
private Path path;
|
||||
private NioAccess nioAccess;
|
||||
|
||||
private SharedFileChannel inTest;
|
||||
private SharedFileChannel otherInTest;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
pathOfTestfile = Files.createTempFile("sharedFileChannelTest", "tmp");
|
||||
writeByteArrayToFile(pathOfTestfile.toFile(), FILE_CONTENT);
|
||||
pathOfOtherTestfile = Files.createTempFile("sharedFileChannelTest", "tmp");
|
||||
writeByteArrayToFile(pathOfOtherTestfile.toFile(), OTHER_FILE_CONTENT);
|
||||
|
||||
assertThat(pathOfTestfile, isFile().withContent(FILE_CONTENT));
|
||||
assertThat(pathOfOtherTestfile, isFile().withContent(OTHER_FILE_CONTENT));
|
||||
|
||||
inTest = new SharedFileChannel(pathOfTestfile);
|
||||
otherInTest = new SharedFileChannel(pathOfOtherTestfile);
|
||||
public void setUp() {
|
||||
path = mock(Path.class);
|
||||
nioAccess = mock(NioAccess.class);
|
||||
inTest = new SharedFileChannel(path, nioAccess);
|
||||
}
|
||||
|
||||
public class ReadFully {
|
||||
public class Open {
|
||||
|
||||
@Test
|
||||
public void testOpenFailsIfPathIsADirectory() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(true);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(format("%s is a directory", path));
|
||||
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenFailsIfFileDoesNotExistAndOpenModeIsRead() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(format("%s does not exist", path));
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
inTest.open(OpenMode.READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFullyReadsFileContents() {
|
||||
byte[] result = new byte[FILE_CONTENT.length];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(result);
|
||||
public void testOpenOpensAChannelIfOpenModeIsReadAndFileExists() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
|
||||
int read = inTest.readFully(0, buffer);
|
||||
inTest.open(OpenMode.READ);
|
||||
|
||||
assertThat(result, is(FILE_CONTENT));
|
||||
assertThat(read, is(FILE_CONTENT.length));
|
||||
verify(nioAccess).open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFullyReadsFromPositionContents() {
|
||||
int position = 5;
|
||||
byte[] result = new byte[FILE_CONTENT.length - position];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(result);
|
||||
public void testOpenIfClosedOpensAChannelIfChannelIsNotOpenOpenModeIsReadAndFileExists() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
|
||||
int read = inTest.readFully(position, buffer);
|
||||
inTest.openIfClosed(OpenMode.READ);
|
||||
|
||||
assertThat(result, is(copyOfRange(FILE_CONTENT, position, FILE_CONTENT.length)));
|
||||
assertThat(read, is(FILE_CONTENT.length - position));
|
||||
verify(nioAccess).open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFullyReadsTillEndIfBufferHasMoreSpaceAvailabe() {
|
||||
int position = 5;
|
||||
byte[] result = new byte[FILE_CONTENT.length];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(result);
|
||||
byte[] expected = copyOf(copyOfRange(FILE_CONTENT, position, FILE_CONTENT.length), FILE_CONTENT.length);
|
||||
public void testOpenOpensAChannelIfOpenModeIsWriteAndFileExists() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
|
||||
int read = inTest.readFully(position, buffer);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
assertThat(result, is(expected));
|
||||
assertThat(read, is(FILE_CONTENT.length - position));
|
||||
verify(nioAccess).open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadReadsNothingIfBufferHasNoSpaceAvailabe() {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(5);
|
||||
buffer.position(5);
|
||||
public void testOpenOpensAChannelIfOpenModeIsWriteAndFileDoesNotExist() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
|
||||
int read = inTest.readFully(0, buffer);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
assertThat(buffer.array(), is(new byte[5]));
|
||||
assertThat(read, is(0));
|
||||
verify(nioAccess).open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadReadsNothingIfPositionIsEndOfFile() {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(5);
|
||||
public void testOpenWrapsExceptionsFromOpeningChannelInUncheckedIOExceptions() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
IOException exceptionFromOpeningChannel = new IOException();
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenThrow(exceptionFromOpeningChannel);
|
||||
|
||||
int read = inTest.readFully(FILE_CONTENT.length, buffer);
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromOpeningChannel));
|
||||
|
||||
assertThat(buffer.array(), is(new byte[5]));
|
||||
assertThat(read, is(EOF));
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadReadsNothingIfAfterEndOfFile() {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(5);
|
||||
public void testOpenFailsIfInvokedTwiceBeforeClose() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
|
||||
int read = inTest.readFully(FILE_CONTENT.length + 10, buffer);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
assertThat(buffer.array(), is(new byte[5]));
|
||||
assertThat(read, is(EOF));
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("already open for current thread");
|
||||
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadThrowsIllegalArgumentExceptionIfPositionIsNegative() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
public void testOpenIfClosedDoesDoNothingIfInvokedOnOpenChannel() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
|
||||
inTest.readFully(-1, ByteBuffer.allocate(0));
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
inTest.openIfClosed(OpenMode.READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWorksIfInvokedTwiceAfterClose() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(mock(FileChannel.class));
|
||||
|
||||
inTest.open(OpenMode.WRITE);
|
||||
inTest.close();
|
||||
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenDoesNotOpenChannelTwiceIfInvokedTwiceByDifferentThreads() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(mock(FileChannel.class));
|
||||
|
||||
inThreadRethrowingException(() -> inTest.open(OpenMode.WRITE));
|
||||
inThreadRethrowingException(() -> inTest.open(OpenMode.WRITE));
|
||||
|
||||
verify(nioAccess).open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Close {
|
||||
|
||||
@Test
|
||||
public void testCloseIfNotOpenFails() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("closed for current thread");
|
||||
|
||||
inTest.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseIfOpenDoesNothingIfNotOpen() {
|
||||
inTest.closeIfOpen();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseIfClosedFails() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(mock(FileChannel.class));
|
||||
inTest.open(OpenMode.WRITE);
|
||||
inTest.close();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("closed for current thread");
|
||||
|
||||
inTest.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseForcesAndClosesChannel() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
inTest.close();
|
||||
|
||||
InOrder inOrder = inOrder(channel, nioAccess);
|
||||
inOrder.verify(channel).force(true);
|
||||
inOrder.verify(nioAccess).close(channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseWrapsIOExceptionFromForceInUncheckedIOExceptionAndStillClosesChannel() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
IOException exceptionFromForce = new IOException();
|
||||
doThrow(exceptionFromForce).when(channel).force(true);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromForce));
|
||||
|
||||
try {
|
||||
inTest.close();
|
||||
} finally {
|
||||
verify(nioAccess).close(channel);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseWrapsIOExceptionFromCloseInUncheckedIOException() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
IOException exceptionFromClose = new IOException();
|
||||
doThrow(exceptionFromClose).when(nioAccess).close(channel);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromClose));
|
||||
|
||||
inTest.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseIfOpenClosesChannelIfOpen() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
inTest.closeIfOpen();
|
||||
|
||||
verify(nioAccess).close(channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseDoesNotCloseChannelIfOpenedTwice() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
inThreadRethrowingException(() -> inTest.open(OpenMode.WRITE));
|
||||
|
||||
inTest.close();
|
||||
|
||||
verify(nioAccess, never()).close(channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastCloseDoesCloseChannelIfOpenedTwice() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
inThreadRethrowingException(() -> {
|
||||
inTest.open(OpenMode.WRITE);
|
||||
inTest.close();
|
||||
});
|
||||
|
||||
inTest.close();
|
||||
|
||||
verify(nioAccess).close(channel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ReadFully {
|
||||
|
||||
@Rule
|
||||
public Timeout timeoutRule = Timeout.seconds(1);
|
||||
|
||||
private FileChannel channel;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFullyWrapsExceptionFromReadInUncheckedIOException() throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(0);
|
||||
IOException exceptionFromRead = new IOException();
|
||||
when(channel.read(buffer, 0)).thenThrow(exceptionFromRead);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromRead));
|
||||
|
||||
inTest.readFully(0, buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFullyDelegatesToChannelRead() throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(50);
|
||||
when(channel.read(buffer, 0)).thenAnswer(new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
buffer.position(50);
|
||||
return 50;
|
||||
}
|
||||
});
|
||||
|
||||
int result = inTest.readFully(0, buffer);
|
||||
|
||||
assertThat(result, is(50));
|
||||
verify(channel).read(buffer, 0);
|
||||
verifyNoMoreInteractions(channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFullyReturnsEofWhenFirstReadReturnsIt() throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(50);
|
||||
when(channel.read(buffer, 0)).thenReturn(EOF);
|
||||
|
||||
int result = inTest.readFully(0, buffer);
|
||||
|
||||
assertThat(result, is(EOF));
|
||||
verify(channel).read(buffer, 0);
|
||||
verifyNoMoreInteractions(channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadStopsReadingIfEofIsReached() throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(50);
|
||||
when(channel.read(buffer, 0)).thenAnswer(simulateRead(20, buffer));
|
||||
when(channel.read(buffer, 20)).thenReturn(EOF);
|
||||
|
||||
int result = inTest.readFully(0, buffer);
|
||||
|
||||
assertThat(result, is(20));
|
||||
verify(channel).read(buffer, 0);
|
||||
verify(channel).read(buffer, 20);
|
||||
verifyNoMoreInteractions(channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFullyInvokesReadUntilBufferIsFull() throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(50);
|
||||
when(channel.read(buffer, 0)).then(simulateRead(20, buffer));
|
||||
when(channel.read(buffer, 20)).then(simulateRead(20, buffer));
|
||||
when(channel.read(buffer, 40)).then(simulateRead(10, buffer));
|
||||
|
||||
int result = inTest.readFully(0, buffer);
|
||||
|
||||
assertThat(result, is(50));
|
||||
verify(channel).read(buffer, 0);
|
||||
verify(channel).read(buffer, 20);
|
||||
verify(channel).read(buffer, 40);
|
||||
verifyNoMoreInteractions(channel);
|
||||
}
|
||||
|
||||
private Answer<Integer> simulateRead(int amount, ByteBuffer target) {
|
||||
return new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
target.position(target.position() + amount);
|
||||
return amount;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Truncate {
|
||||
|
||||
private FileChannel channel;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
public void setUp() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateToZeroDeletesFileContent() {
|
||||
inTest.truncate(0);
|
||||
inTest.close();
|
||||
public void testTruncateDelegatesToChannelTruncate() throws IOException {
|
||||
int truncateTo = 32;
|
||||
|
||||
assertThat(pathOfTestfile, isFile().thatIsEmpty());
|
||||
inTest.truncate(truncateTo);
|
||||
|
||||
verify(channel).truncate(truncateTo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateToSmallerSizeTruncatesFile() {
|
||||
inTest.truncate(5);
|
||||
inTest.close();
|
||||
public void testTruncateWrapsIOExceptionInUncheckedIOException() throws IOException {
|
||||
int truncateTo = 32;
|
||||
IOException exceptionFromTruncate = new IOException();
|
||||
when(channel.truncate(truncateTo)).thenThrow(exceptionFromTruncate);
|
||||
|
||||
assertThat(pathOfTestfile, isFile().withContent(copyOfRange(FILE_CONTENT, 0, 5)));
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromTruncate));
|
||||
|
||||
inTest.truncate(truncateTo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateToGreaterSizeDoesNotChangeFile() {
|
||||
inTest.truncate(FILE_CONTENT.length + 10);
|
||||
inTest.close();
|
||||
|
||||
assertThat(pathOfTestfile, isFile().withContent(FILE_CONTENT));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeReturnsFileSize() {
|
||||
inTest.open(OpenMode.READ);
|
||||
public class Size {
|
||||
|
||||
private FileChannel channel;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeDelegatesToChannelSize() throws IOException {
|
||||
long expectedSize = 832;
|
||||
when(channel.size()).thenReturn(expectedSize);
|
||||
|
||||
long result = inTest.size();
|
||||
|
||||
assertThat(result, is(expectedSize));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWrapsIOExceptionInUncheckedIOException() throws IOException {
|
||||
IOException exceptionFromSize = new IOException();
|
||||
when(channel.size()).thenThrow(exceptionFromSize);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromSize));
|
||||
|
||||
inTest.size();
|
||||
}
|
||||
|
||||
assertThat(inTest.size(), is((long) FILE_CONTENT.length));
|
||||
}
|
||||
|
||||
public class TransferTo {
|
||||
|
||||
private FileChannel channel;
|
||||
|
||||
private Path targetPath;
|
||||
private SharedFileChannel targetInTest;
|
||||
private FileChannel targetChannel;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
inTest.open(OpenMode.READ);
|
||||
otherInTest.open(OpenMode.WRITE);
|
||||
public void setUp() throws IOException {
|
||||
targetPath = mock(Path.class);
|
||||
targetInTest = new SharedFileChannel(targetPath, nioAccess);
|
||||
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
when(nioAccess.isDirectory(targetPath)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(targetPath)).thenReturn(true);
|
||||
targetChannel = mock(FileChannel.class);
|
||||
when(nioAccess.open(targetPath, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(targetChannel);
|
||||
targetInTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransferToCopiesSelectedRegionToTargetPosition() {
|
||||
// TODO Markus Kreusch
|
||||
public void testTransferToThrowsIllegalArugmentExceptionIfCountIsNegative() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Count must not be negative");
|
||||
|
||||
inTest.transferTo(0, -1, targetInTest, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransferToDoesNothingIfCountIsZero() {
|
||||
// TODO Markus Kreusch
|
||||
public void testTransferToSetsPositionOfTargetChannelAndThenDelegatesToChannelsTransferTo() throws IOException {
|
||||
long targetPosition = 43L;
|
||||
long startPosition = 22L;
|
||||
long count = 39L;
|
||||
when(channel.transferTo(startPosition, count, targetChannel)).thenReturn(count);
|
||||
when(channel.size()).thenReturn(startPosition + count);
|
||||
|
||||
long result = inTest.transferTo(startPosition, count, targetInTest, targetPosition);
|
||||
|
||||
assertThat(result, is(count));
|
||||
InOrder inOrder = inOrder(channel, targetChannel);
|
||||
inOrder.verify(targetChannel).position(targetPosition);
|
||||
inOrder.verify(channel).transferTo(startPosition, count, targetChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransferToTransfersLessThanCountBytesIfEndOfSourceFileIsReached() {
|
||||
// TODO Markus Kreusch
|
||||
public void testTransferToInvokesTransferUntilAllBytesHaveBeenTransferred() throws IOException {
|
||||
long targetPosition = 43L;
|
||||
long startPosition = 22L;
|
||||
long count = 39L;
|
||||
long firstTransferCount = 10L;
|
||||
long secondTransferCount = 7L;
|
||||
long thridTransferCount = count - firstTransferCount - secondTransferCount;
|
||||
when(channel.transferTo(startPosition, count, targetChannel)).thenReturn(firstTransferCount);
|
||||
when(channel.transferTo(startPosition + firstTransferCount, count - firstTransferCount, targetChannel)).thenReturn(secondTransferCount);
|
||||
when(channel.transferTo(startPosition + firstTransferCount + secondTransferCount, thridTransferCount, targetChannel)).thenReturn(thridTransferCount);
|
||||
when(channel.size()).thenReturn(startPosition + count);
|
||||
|
||||
long result = inTest.transferTo(startPosition, count, targetInTest, targetPosition);
|
||||
|
||||
assertThat(result, is(count));
|
||||
InOrder inOrder = inOrder(channel, targetChannel);
|
||||
inOrder.verify(targetChannel).position(targetPosition);
|
||||
inOrder.verify(channel).transferTo(startPosition, count, targetChannel);
|
||||
inOrder.verify(channel).transferTo(startPosition + firstTransferCount, count - firstTransferCount, targetChannel);
|
||||
inOrder.verify(channel).transferTo(startPosition + firstTransferCount + secondTransferCount, thridTransferCount, targetChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransferToThrowsIllegalArgumentExceptionIfPositionIsSmallerZero() {
|
||||
// TODO Markus Kreusch
|
||||
public void testTransferToStopsTransferAtEndOfSourceFile() throws IOException {
|
||||
long targetPosition = 43L;
|
||||
long startPosition = 22L;
|
||||
long count = 39L;
|
||||
long countAvailable = 30L;
|
||||
when(channel.transferTo(startPosition, countAvailable, targetChannel)).thenReturn(countAvailable);
|
||||
when(channel.size()).thenReturn(startPosition + countAvailable);
|
||||
|
||||
long result = inTest.transferTo(startPosition, count, targetInTest, targetPosition);
|
||||
|
||||
assertThat(result, is(countAvailable));
|
||||
InOrder inOrder = inOrder(channel, targetChannel);
|
||||
inOrder.verify(targetChannel).position(targetPosition);
|
||||
inOrder.verify(channel).transferTo(startPosition, countAvailable, targetChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransferToThrowsIllegalArgumentExceptionIfCountIsSmallerZero() {
|
||||
// TODO Markus Kreusch
|
||||
public void testTransferToWrapsIOExceptionFromPositionInUncheckedIOException() throws IOException {
|
||||
when(channel.size()).thenReturn(Long.MAX_VALUE);
|
||||
IOException exceptionFromPosition = new IOException();
|
||||
when(targetChannel.position(anyLong())).thenThrow(exceptionFromPosition);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromPosition));
|
||||
|
||||
inTest.transferTo(0L, 10L, targetInTest, 0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransferToThrowsIllegalArgumentExceptionIfTargetPositionIsSmallerZero() {
|
||||
// TODO Markus Kreusch
|
||||
}
|
||||
public void testTransferToWrapsIOExceptionFromTransferToInUncheckedIOException() throws IOException {
|
||||
when(channel.size()).thenReturn(Long.MAX_VALUE);
|
||||
IOException exceptionFromTransferTo = new IOException();
|
||||
when(channel.transferTo(anyLong(), anyLong(), any())).thenThrow(exceptionFromTransferTo);
|
||||
|
||||
@Test
|
||||
public void testTransferToTransfersSelectedRegionToTargetAfterEndOfFile() {
|
||||
// TODO Markus Kreusch
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromTransferTo));
|
||||
|
||||
inTest.transferTo(0L, 10L, targetInTest, 0L);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class WriteFully {
|
||||
|
||||
@Rule
|
||||
public Timeout timeoutRule = Timeout.seconds(1);
|
||||
|
||||
private FileChannel channel;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
public void setUp() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFullyWritesFileContents() {
|
||||
// TODO Markus Kreusch
|
||||
public void testWriteFullyWrapsIOExceptionFromWriteIntoUncheckedIOException() throws IOException {
|
||||
int count = 1;
|
||||
int position = 0;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(count);
|
||||
IOException exceptionFromWrite = new IOException();
|
||||
when(channel.write(buffer, position)).thenThrow(exceptionFromWrite);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromWrite));
|
||||
|
||||
inTest.writeFully(position, buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFullyWritesContentsToPosition() {
|
||||
// TODO Markus Kreusch
|
||||
public void testWriteFullyDelegatesToChannelsWrite() throws IOException {
|
||||
int count = 50;
|
||||
int position = 31;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(count);
|
||||
when(channel.write(buffer, position)).then(simulateWrite(count, buffer));
|
||||
|
||||
int result = inTest.writeFully(position, buffer);
|
||||
|
||||
assertThat(result, is(count));
|
||||
verify(channel).write(buffer, position);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFullyWritesContentsEvenIfPositionIsGreaterCurrentEndOfFile() {
|
||||
// TODO Markus Kreusch
|
||||
public void testWriteFullyDelegatesToWriteUntilAllBytesFromBufferHaveBeenWritten() throws IOException {
|
||||
int count = 50;
|
||||
int countOfFirstWrite = 10;
|
||||
int countOfSecondWrite = 15;
|
||||
int countOfThridWrite = count - countOfFirstWrite - countOfSecondWrite;
|
||||
int position = 31;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(count);
|
||||
when(channel.write(buffer, position)).then(simulateWrite(countOfFirstWrite, buffer));
|
||||
when(channel.write(buffer, position + countOfFirstWrite)).then(simulateWrite(countOfSecondWrite, buffer));
|
||||
when(channel.write(buffer, position + countOfFirstWrite + countOfSecondWrite)).then(simulateWrite(countOfThridWrite, buffer));
|
||||
|
||||
int result = inTest.writeFully(position, buffer);
|
||||
|
||||
assertThat(result, is(count));
|
||||
verify(channel).write(buffer, position);
|
||||
verify(channel).write(buffer, position + countOfFirstWrite);
|
||||
verify(channel).write(buffer, position + countOfFirstWrite + countOfSecondWrite);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteWritesNothingBufferHasNothingAvailabe() {
|
||||
// TODO Markus Kreusch
|
||||
public void testWriteFullyDelegatesToWriteASingleTimeEvenIfBytesHasNotBytesRemaing() throws IOException {
|
||||
int count = 0;
|
||||
int position = 31;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(count);
|
||||
when(channel.write(buffer, position)).then(simulateWrite(count, buffer));
|
||||
|
||||
int result = inTest.writeFully(position, buffer);
|
||||
|
||||
assertThat(result, is(count));
|
||||
verify(channel).write(buffer, position);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteThrowsIllegalArgumentExceptionIfPositionIsNegative() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
|
||||
inTest.writeFully(-1, ByteBuffer.allocate(0));
|
||||
private Answer<Integer> simulateWrite(int amount, ByteBuffer target) {
|
||||
return new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
target.position(target.position() + amount);
|
||||
return amount;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class MultipleOpenInvocation {
|
||||
public class OperationsFailingIfClosed {
|
||||
|
||||
@Test
|
||||
public void testInvokeOpenMultipleTimesFromSameThreadThrowsException() {
|
||||
inTest.open(OpenMode.READ);
|
||||
public void testReadFullyFailsIfNotOpen() {
|
||||
ByteBuffer irrelevant = null;
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("A thread can only open a SharedFileChannel once");
|
||||
thrown.expectMessage("closed for current thread");
|
||||
|
||||
inTest.open(OpenMode.READ);
|
||||
inTest.readFully(0, irrelevant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeOpenMultipleTimesFromDifferentThreadsWorks() {
|
||||
inTest.open(OpenMode.READ);
|
||||
inThreadRethrowingExceptions(() -> inTest.open(OpenMode.READ));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class NonOpenChannel {
|
||||
|
||||
@Test
|
||||
public void testClosingNonOpenChannelThrowsException() {
|
||||
public void testTruncateFailsIfNotOpen() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("SharedFileChannel closed for current thread");
|
||||
|
||||
inTest.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokingReadFullyOnNonOpenChannelThrowsException() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("SharedFileChannel closed for current thread");
|
||||
|
||||
inTest.readFully(0, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokingTruncateOnNonOpenChannelThrowsException() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("SharedFileChannel closed for current thread");
|
||||
thrown.expectMessage("closed for current thread");
|
||||
|
||||
inTest.truncate(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokingSizeOnNonOpenChannelThrowsException() {
|
||||
public void testSizeFailsIfNotOpen() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("SharedFileChannel closed for current thread");
|
||||
thrown.expectMessage("closed for current thread");
|
||||
|
||||
inTest.size();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokingTransferToOnNonOpenChannelThrowsException() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("SharedFileChannel closed for current thread");
|
||||
public void testTransferToFailsIfSourceNotOpen() {
|
||||
SharedFileChannel irrelevant = null;
|
||||
|
||||
inTest.transferTo(0, 1, null, 0);
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("closed for current thread");
|
||||
|
||||
inTest.transferTo(0, 0, irrelevant, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokingTransferToPassingNonOpenChannelThrowsException() {
|
||||
public void testTransferToFailsIfTargetNotOpen() {
|
||||
Path targetPath = mock(Path.class);
|
||||
SharedFileChannel targetInTest = new SharedFileChannel(targetPath, nioAccess);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("SharedFileChannel closed for current thread");
|
||||
thrown.expectMessage("closed for current thread");
|
||||
|
||||
inTest.open(OpenMode.READ);
|
||||
|
||||
inTest.transferTo(0, 1, otherInTest, 0);
|
||||
inTest.transferTo(0, 0, targetInTest, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokinWriteFullyOnNonOpenChannelThrowsException() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("SharedFileChannel closed for current thread");
|
||||
public void testWriteFullyFailsIfNotOpen() {
|
||||
ByteBuffer irrelevant = null;
|
||||
|
||||
inTest.writeFully(0, null);
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("closed for current thread");
|
||||
|
||||
inTest.writeFully(0, irrelevant);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
@SuppressWarnings("deprecation")
|
||||
public void teardown() {
|
||||
inTest.forceClose();
|
||||
otherInTest.forceClose();
|
||||
try {
|
||||
Files.delete(pathOfTestfile);
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
Files.delete(pathOfOtherTestfile);
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void inThreadRethrowingExceptions(Runnable task) {
|
||||
Holder<RuntimeException> holder = new Holder<>();
|
||||
private void inThreadRethrowingException(Runnable task) {
|
||||
Holder<Throwable> exception = new Holder<>(null);
|
||||
Thread thread = new Thread(() -> {
|
||||
try {
|
||||
task.run();
|
||||
} catch (RuntimeException e) {
|
||||
holder.value = e;
|
||||
} catch (Throwable e) {
|
||||
exception.set(e);
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (holder.value != null) {
|
||||
throw holder.value;
|
||||
rethrowUnchecked(exception.get());
|
||||
}
|
||||
|
||||
private void rethrowUnchecked(Throwable exception) {
|
||||
if (exception instanceof RuntimeException) {
|
||||
throw (RuntimeException) exception;
|
||||
} else if (exception instanceof Error) {
|
||||
throw (Error) exception;
|
||||
} else if (exception != null) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+453
-203
@@ -1,5 +1,7 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import static org.cryptomator.filesystem.nio.OpenMode.WRITE;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -11,11 +13,15 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -26,228 +32,472 @@ import org.mockito.InOrder;
|
||||
import de.bechte.junit.runners.context.HierarchicalContextRunner;
|
||||
|
||||
@RunWith(HierarchicalContextRunner.class)
|
||||
@SuppressWarnings("resource")
|
||||
public class WritableNioFileTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private NioFile file;
|
||||
private FileSystem fileSystem;
|
||||
|
||||
private Path path;
|
||||
|
||||
private SharedFileChannel channel;
|
||||
|
||||
private ReadWriteLock lock;
|
||||
private Runnable afterCloseCallback;
|
||||
|
||||
private Lock writeLock;
|
||||
private NioAccess nioAccess;
|
||||
|
||||
private WritableNioFile inTest;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
file = mock(NioFile.class);
|
||||
fileSystem = mock(FileSystem.class);
|
||||
channel = mock(SharedFileChannel.class);
|
||||
lock = mock(ReadWriteLock.class);
|
||||
writeLock = mock(Lock.class);
|
||||
|
||||
when(file.channel()).thenReturn(channel);
|
||||
when(file.lock()).thenReturn(lock);
|
||||
when(lock.writeLock()).thenReturn(writeLock);
|
||||
path = mock(Path.class);
|
||||
afterCloseCallback = mock(Runnable.class);
|
||||
nioAccess = mock(NioAccess.class);
|
||||
inTest = new WritableNioFile(fileSystem, path, channel, afterCloseCallback, nioAccess);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFailsIfClosed() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
inTest.close();
|
||||
ByteBuffer irrelevant = null;
|
||||
public class ConstructorTests {
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.write(irrelevant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositionFailsIfClosed() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
inTest.close();
|
||||
long irrelevant = 1023;
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.position(irrelevant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOpenReturnsTrueForNewInstance() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
|
||||
assertThat(inTest.isOpen(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOpenReturnsFalseForClosedInstance() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
inTest.close();
|
||||
|
||||
assertThat(inTest.isOpen(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInvokesChannelsOpenWithModeWriteIfInvokedForTheFirstTimeBeforeInvokingWriteFully() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
ByteBuffer irrelevant = null;
|
||||
|
||||
inTest.write(irrelevant);
|
||||
|
||||
InOrder inOrder = inOrder(channel);
|
||||
inOrder.verify(channel).open(WRITE);
|
||||
inOrder.verify(channel).writeFully(0, irrelevant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteDoesNotModifyBuffer() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
|
||||
inTest.write(buffer);
|
||||
|
||||
verifyZeroInteractions(buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInvokesWriteFullyWithZeroPositionIfNotSet() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
|
||||
inTest.write(buffer);
|
||||
|
||||
verify(channel).writeFully(0, buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInvokesWriteFullyWithSetPosition() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
long position = 10;
|
||||
inTest.position(position);
|
||||
|
||||
inTest.write(buffer);
|
||||
|
||||
verify(channel).writeFully(position, buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInvokesWriteFullyWithEndOfPreviousWriteIfInvokedTwice() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
int endOfPreviousWrite = 10;
|
||||
when(channel.writeFully(0, buffer)).thenReturn(endOfPreviousWrite);
|
||||
|
||||
inTest.write(buffer);
|
||||
inTest.write(buffer);
|
||||
|
||||
verify(channel).writeFully(endOfPreviousWrite, buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReturnsResultOfWriteFully() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
int resultOfWriteFully = 14;
|
||||
when(channel.writeFully(0, buffer)).thenReturn(resultOfWriteFully);
|
||||
|
||||
int result = inTest.write(buffer);
|
||||
|
||||
assertThat(result, is(resultOfWriteFully));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateFailsIfNotOpen() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
inTest.close();
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.truncate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateInvokesChannelsOpenWithModeWriteIfInvokedForTheFirstTimeBeforeInvokingTruncate() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
|
||||
inTest.truncate();
|
||||
|
||||
InOrder inOrder = inOrder(channel);
|
||||
inOrder.verify(channel).open(WRITE);
|
||||
inOrder.verify(channel).truncate(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateChannelsTruncateWithZeroAsParameter() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
|
||||
inTest.truncate();
|
||||
|
||||
verify(channel).truncate(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseClosesChannelIfOpenedAndUnlocksWriteLock() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
inTest.truncate();
|
||||
|
||||
inTest.close();
|
||||
|
||||
InOrder inOrder = inOrder(channel, writeLock);
|
||||
inOrder.verify(channel).close();
|
||||
inOrder.verify(writeLock).unlock();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseDoesNothingOnSecondInvocation() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
inTest.truncate();
|
||||
|
||||
inTest.close();
|
||||
inTest.close();
|
||||
|
||||
InOrder inOrder = inOrder(channel, writeLock);
|
||||
inOrder.verify(channel).close();
|
||||
inOrder.verify(writeLock).unlock();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseOnlyUnlocksWriteLockIfChannelNotOpen() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
|
||||
inTest.close();
|
||||
|
||||
verify(writeLock).unlock();
|
||||
verifyZeroInteractions(channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseUnlocksWriteLockEvenIfCloseThrowsException() {
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
inTest.truncate();
|
||||
String message = "exceptionMessage";
|
||||
doThrow(new RuntimeException(message)).when(channel).close();
|
||||
|
||||
thrown.expectMessage(message);
|
||||
|
||||
try {
|
||||
inTest.close();
|
||||
} finally {
|
||||
verify(writeLock).unlock();
|
||||
@Test
|
||||
public void testConstructorSetsFileSystem() {
|
||||
assertThat(inTest.fileSystem(), is(fileSystem));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorSetsPath() {
|
||||
assertThat(inTest.path(), is(path));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorSetsChannel() {
|
||||
assertThat(inTest.channel(), is(channel));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class WriteTests {
|
||||
|
||||
@Test
|
||||
public void testWriteOpensChannelIfClosedBeforeInvokingWriteFully() {
|
||||
ByteBuffer irrelevant = null;
|
||||
|
||||
inTest.write(irrelevant);
|
||||
|
||||
InOrder inOrder = inOrder(channel);
|
||||
inOrder.verify(channel).openIfClosed(WRITE);
|
||||
inOrder.verify(channel).writeFully(0, irrelevant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteDoesNotModifyBuffer() {
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
|
||||
inTest.write(buffer);
|
||||
|
||||
verifyZeroInteractions(buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInvokesWriteFullyWithZeroPositionIfNotSet() {
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
|
||||
inTest.write(buffer);
|
||||
|
||||
verify(channel).writeFully(0, buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInvokesWriteFullyWithSetPosition() {
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
long position = 10;
|
||||
inTest.position(position);
|
||||
|
||||
inTest.write(buffer);
|
||||
|
||||
verify(channel).writeFully(position, buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInvokesWriteFullyWithEndOfPreviousWriteIfInvokedTwice() {
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
int endOfPreviousWrite = 10;
|
||||
when(channel.writeFully(0, buffer)).thenReturn(endOfPreviousWrite);
|
||||
|
||||
inTest.write(buffer);
|
||||
inTest.write(buffer);
|
||||
|
||||
verify(channel).writeFully(endOfPreviousWrite, buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReturnsResultOfWriteFully() {
|
||||
ByteBuffer buffer = mock(ByteBuffer.class);
|
||||
int resultOfWriteFully = 14;
|
||||
when(channel.writeFully(0, buffer)).thenReturn(resultOfWriteFully);
|
||||
|
||||
int result = inTest.write(buffer);
|
||||
|
||||
assertThat(result, is(resultOfWriteFully));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class MoveToTests {
|
||||
|
||||
private WritableNioFile target;
|
||||
|
||||
private Path pathOfTarget;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
target = mock(WritableNioFile.class);
|
||||
pathOfTarget = mock(Path.class);
|
||||
when(target.fileSystem()).thenReturn(fileSystem);
|
||||
when(target.path()).thenReturn(pathOfTarget);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToFailsIfTargetIsNoWritableNioFile() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Can only move to a WritableFile from the same FileSystem");
|
||||
|
||||
inTest.moveTo(mock(WritableFile.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToFailsIfTargetIsNotFromSameFileSystem() {
|
||||
WritableNioFile targetFromOtherFileSystem = mock(WritableNioFile.class);
|
||||
when(targetFromOtherFileSystem.fileSystem()).thenReturn(mock(FileSystem.class));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Can only move to a WritableFile from the same FileSystem");
|
||||
|
||||
inTest.moveTo(mock(WritableFile.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToFailsIfSourceIsDirectory() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(true);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("Source is a directory");
|
||||
thrown.expectMessage(path.toString());
|
||||
thrown.expectMessage(pathOfTarget.toString());
|
||||
|
||||
inTest.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToFailsIfTargetIsDirectory() {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(true);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("Target is a directory");
|
||||
thrown.expectMessage(path.toString());
|
||||
thrown.expectMessage(pathOfTarget.toString());
|
||||
|
||||
inTest.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToDoesNothingIfTargetIsSameInstance() {
|
||||
inTest.moveTo(inTest);
|
||||
|
||||
verifyZeroInteractions(nioAccess);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToAssertsTargetIsOpenClosesChannelsIfOpenMovesFileAndInvokesAfterCloseCallbacks() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(false);
|
||||
|
||||
inTest.moveTo(target);
|
||||
|
||||
InOrder inOrder = inOrder(target, nioAccess, channel, afterCloseCallback);
|
||||
inOrder.verify(target).assertOpen();
|
||||
inOrder.verify(channel).closeIfOpen();
|
||||
inOrder.verify(target).closeChannelIfOpened();
|
||||
inOrder.verify(nioAccess).move(path, pathOfTarget, REPLACE_EXISTING);
|
||||
inOrder.verify(target).invokeAfterCloseCallback();
|
||||
inOrder.verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToWrapsIOExceptionFromMoveInUncheckedIOException() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(false);
|
||||
IOException exceptionFromMove = new IOException();
|
||||
doThrow(exceptionFromMove).when(nioAccess).move(path, pathOfTarget, REPLACE_EXISTING);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromMove));
|
||||
|
||||
inTest.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToInvokesAfterCloseCallbacksEvenIfMoveToThrowsException() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(false);
|
||||
IOException exceptionFromMove = new IOException();
|
||||
doThrow(exceptionFromMove).when(nioAccess).move(path, pathOfTarget, REPLACE_EXISTING);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromMove));
|
||||
|
||||
inTest.moveTo(target);
|
||||
|
||||
verify(target).invokeAfterCloseCallback();
|
||||
verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SetLastModifiedTests {
|
||||
|
||||
@Test
|
||||
public void testSetLastModifiedOpensChannelIfClosedAndSetsLastModifiedTime() throws IOException {
|
||||
Instant instant = Instant.parse("2016-01-05T13:51:00Z");
|
||||
FileTime time = FileTime.from(instant);
|
||||
|
||||
inTest.setLastModified(instant);
|
||||
|
||||
InOrder inOrder = inOrder(channel, nioAccess);
|
||||
inOrder.verify(channel).openIfClosed(OpenMode.WRITE);
|
||||
inOrder.verify(nioAccess).setLastModifiedTime(path, time);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetLastModifiedWrapsIOExceptionFromSetLastModifiedInUncheckedIOException() throws IOException {
|
||||
IOException exceptionFromSetLastModified = new IOException();
|
||||
Instant instant = Instant.parse("2016-01-05T13:51:00Z");
|
||||
FileTime time = FileTime.from(instant);
|
||||
doThrow(exceptionFromSetLastModified).when(nioAccess).setLastModifiedTime(path, time);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromSetLastModified));
|
||||
|
||||
inTest.setLastModified(instant);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DeleteTests {
|
||||
|
||||
@Test
|
||||
public void testDeleteClosesChannelIfOpenAndDeletesFileAndInvokesAfterCloseCallback() throws IOException {
|
||||
inTest.delete();
|
||||
|
||||
InOrder inOrder = inOrder(channel, nioAccess, afterCloseCallback);
|
||||
inOrder.verify(channel).closeIfOpen();
|
||||
inOrder.verify(nioAccess).delete(path);
|
||||
inOrder.verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteClosesWritableNioFile() {
|
||||
inTest.delete();
|
||||
|
||||
assertThat(inTest.isOpen(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteClosesWritableNioFileEvenIfDeleteFails() throws IOException {
|
||||
IOException exceptionFromDelete = new IOException();
|
||||
doThrow(exceptionFromDelete).when(nioAccess).delete(path);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromDelete));
|
||||
|
||||
try {
|
||||
inTest.delete();
|
||||
} finally {
|
||||
assertThat(inTest.isOpen(), is(false));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteInvokesAfterCloseCallbackEvenIfDeleteFails() throws IOException {
|
||||
IOException exceptionFromDelete = new IOException();
|
||||
doThrow(exceptionFromDelete).when(nioAccess).delete(path);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromDelete));
|
||||
|
||||
try {
|
||||
inTest.delete();
|
||||
} finally {
|
||||
verify(afterCloseCallback).run();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class TruncateTests {
|
||||
|
||||
@Test
|
||||
public void testTruncateInvokesChannelsOpenWithModeWriteIfInvokedForTheFirstTimeBeforeInvokingTruncate() {
|
||||
inTest.truncate();
|
||||
|
||||
InOrder inOrder = inOrder(channel);
|
||||
inOrder.verify(channel).openIfClosed(WRITE);
|
||||
inOrder.verify(channel).truncate(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateChannelsTruncateWithZeroAsParameter() {
|
||||
inTest.truncate();
|
||||
|
||||
verify(channel).truncate(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CloseTests {
|
||||
|
||||
@Test
|
||||
public void testCloseClosesChannelIfOpenedAndInvokesAfterCloseCallback() {
|
||||
inTest.truncate();
|
||||
|
||||
inTest.close();
|
||||
|
||||
InOrder inOrder = inOrder(channel, afterCloseCallback);
|
||||
inOrder.verify(channel).closeIfOpen();
|
||||
inOrder.verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseDoesNothingOnSecondInvocation() {
|
||||
inTest.truncate();
|
||||
|
||||
inTest.close();
|
||||
inTest.close();
|
||||
|
||||
InOrder inOrder = inOrder(channel, afterCloseCallback);
|
||||
inOrder.verify(channel).closeIfOpen();
|
||||
verify(afterCloseCallback).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseInvokesAfterCloseCallbackEvenIfCloseThrowsException() {
|
||||
inTest.truncate();
|
||||
String message = "exceptionMessage";
|
||||
doThrow(new RuntimeException(message)).when(channel).closeIfOpen();
|
||||
|
||||
thrown.expectMessage(message);
|
||||
|
||||
try {
|
||||
inTest.close();
|
||||
} finally {
|
||||
verify(afterCloseCallback).run();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class IsOpenTests {
|
||||
|
||||
@Test
|
||||
public void testIsOpenReturnsTrueForNewInstance() {
|
||||
assertThat(inTest.isOpen(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOpenReturnsFalseForClosedInstance() {
|
||||
inTest.close();
|
||||
|
||||
assertThat(inTest.isOpen(), is(false));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class OperationsFailIfClosedTests {
|
||||
|
||||
@Test
|
||||
public void testWriteFailsIfClosed() {
|
||||
inTest.close();
|
||||
ByteBuffer irrelevant = null;
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.write(irrelevant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositionFailsIfClosed() {
|
||||
inTest.close();
|
||||
long irrelevant = 1023;
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.position(irrelevant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateFailsIfClosed() {
|
||||
inTest.close();
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.truncate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToFailsIfClosed() {
|
||||
inTest.close();
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.moveTo(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetLastModifiedFailsIfClosed() {
|
||||
inTest.close();
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.setLastModified(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteFailsIfClosed() {
|
||||
inTest.close();
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage("already closed");
|
||||
|
||||
inTest.delete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnsureChannelIsOpenedInvokesChannelOpenIfClosedWithModeWrite() {
|
||||
inTest.ensureChannelIsOpened();
|
||||
|
||||
verify(channel).openIfClosed(WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseChannelIfOpenInvokesChannelsCloseIfOpen() {
|
||||
inTest.closeChannelIfOpened();
|
||||
|
||||
verify(channel).closeIfOpen();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
String fileToString = file.toString();
|
||||
WritableNioFile inTest = new WritableNioFile(file);
|
||||
|
||||
assertThat(inTest.toString(), is("Writable" + fileToString));
|
||||
assertThat(inTest.toString(), is(format("WritableNioFile(%s)", path)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
package org.cryptomator.filesystem.nio.integrationtests;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
package org.cryptomator.filesystem.nio.integrationtests;
|
||||
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.emptyFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.file;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.folder;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.testFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.PathMatcher.doesNotExist;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.PathMatcher.isFile;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.nio.NioFileSystem;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
@Ignore
|
||||
public class NioFileIntegrationTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testExistsForExistingFile() {
|
||||
File existingFile = NioFileSystem.rootedAt(testFilesystem(file("testFile"))) //
|
||||
.file("testFile");
|
||||
|
||||
assertThat(existingFile.exists(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsForNonExistingFile() {
|
||||
File nonExistingFile = NioFileSystem.rootedAt(emptyFilesystem()) //
|
||||
.file("testFile");
|
||||
|
||||
assertThat(nonExistingFile.exists(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsForFileWhichIsAFolder() {
|
||||
File fileWhichIsAFolder = NioFileSystem.rootedAt(testFilesystem(folder("nameOfAnExistingFolder"))) //
|
||||
.file("nameOfAnExistingFolder");
|
||||
|
||||
assertThat(fileWhichIsAFolder.exists(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedForExistingFile() {
|
||||
Instant expectedLastModified = Instant.parse("2015-12-31T15:03:34Z");
|
||||
File existingFile = NioFileSystem
|
||||
.rootedAt(testFilesystem( //
|
||||
file("testFile").withLastModified(expectedLastModified))) //
|
||||
.file("testFile");
|
||||
|
||||
assertThat(existingFile.lastModified(), is(expectedLastModified));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedForNonExistingFile() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path pathOfNonExistingFile = filesystemPath.resolve("nonExistingFile");
|
||||
File nonExistingFile = NioFileSystem.rootedAt(filesystemPath) //
|
||||
.file("nonExistingFile");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(pathOfNonExistingFile.toString());
|
||||
|
||||
nonExistingFile.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedForFileWhichIsAFolder() {
|
||||
Path filesystemPath = testFilesystem(folder("nameOfAnExistingFolder"));
|
||||
Path pathOfNonExistingFile = filesystemPath.resolve("nameOfAnExistingFolder");
|
||||
File fileWhichIsAFolder = NioFileSystem.rootedAt(filesystemPath) //
|
||||
.file("nameOfAnExistingFolder");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(pathOfNonExistingFile.toString());
|
||||
|
||||
fileWhichIsAFolder.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToNonExistingTarget() {
|
||||
Path filesystemPath = testFilesystem(file("sourceFile").withData("fileContents"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path sourceFilePath = filesystemPath.resolve("sourceFile");
|
||||
Path targetFilePath = filesystemPath.resolve("targetFile");
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("targetFile");
|
||||
|
||||
source.copyTo(target);
|
||||
|
||||
assertThat(sourceFilePath, isFile().withContent("fileContents"));
|
||||
assertThat(targetFilePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToExistingTarget() {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file("sourceFile").withData("fileContents"), //
|
||||
file("targetFile").withData("wrongFileContents"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path sourceFilePath = filesystemPath.resolve("sourceFile");
|
||||
Path targetFilePath = filesystemPath.resolve("targetFile");
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("targetFile");
|
||||
|
||||
source.copyTo(target);
|
||||
|
||||
assertThat(sourceFilePath, isFile().withContent("fileContents"));
|
||||
assertThat(targetFilePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToFolderTarget() {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file("sourceFile").withData("fileContents"), //
|
||||
folder("aFolderName"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path targetFilePath = filesystemPath.resolve("aFolderName").toAbsolutePath();
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("aFolderName");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(targetFilePath.toAbsolutePath().toString());
|
||||
|
||||
source.copyTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToOfNonExistingFile() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path filePath = filesystemPath.resolve("nonExistingFile").toAbsolutePath();
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
File nonExistingFile = fileSystem.file("nonExistingFile");
|
||||
File target = fileSystem.file("target");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filePath.toString());
|
||||
|
||||
nonExistingFile.copyTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToOfFileWhichIsAFolder() {
|
||||
Path filesystemPath = testFilesystem(folder("folderName"));
|
||||
Path filePath = filesystemPath.resolve("folderName").toAbsolutePath();
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
File fileWhichIsAFolder = fileSystem.file("folderName");
|
||||
File target = fileSystem.file("target");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filePath.toString());
|
||||
|
||||
fileWhichIsAFolder.copyTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToNonExistingTarget() {
|
||||
Path filesystemPath = testFilesystem(file("sourceFile").withData("fileContents"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path sourceFilePath = filesystemPath.resolve("sourceFile");
|
||||
Path targetFilePath = filesystemPath.resolve("targetFile");
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("targetFile");
|
||||
|
||||
source.moveTo(target);
|
||||
|
||||
assertThat(sourceFilePath, doesNotExist());
|
||||
assertThat(targetFilePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToExistingTarget() {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file("sourceFile").withData("fileContents"), //
|
||||
file("targetFile").withData("wrongFileContents"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path sourceFilePath = filesystemPath.resolve("sourceFile");
|
||||
Path targetFilePath = filesystemPath.resolve("targetFile");
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("targetFile");
|
||||
|
||||
source.moveTo(target);
|
||||
|
||||
assertThat(sourceFilePath, doesNotExist());
|
||||
assertThat(targetFilePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToSameFile() {
|
||||
Path filesystemPath = testFilesystem(file("fileName").withData("fileContents"));
|
||||
Path filePath = filesystemPath.resolve("fileName");
|
||||
File file = NioFileSystem.rootedAt(filesystemPath).file("fileName");
|
||||
|
||||
file.moveTo(file);
|
||||
|
||||
assertThat(filePath, isFile().withContent("fileContents"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToDirectoryTarget() {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file("sourceFile").withData("fileContents"), //
|
||||
folder("aFolderName"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Path targetFilePath = filesystemPath.resolve("aFolderName").toAbsolutePath();
|
||||
File source = fileSystem.file("sourceFile");
|
||||
File target = fileSystem.file("aFolderName");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(targetFilePath.toAbsolutePath().toString());
|
||||
|
||||
source.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfNonExistingFile() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path filePath = filesystemPath.resolve("nonExistingFile").toAbsolutePath();
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
File nonExistingFile = fileSystem.file("nonExistingFile");
|
||||
File target = fileSystem.file("target");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filePath.toString());
|
||||
|
||||
nonExistingFile.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFileWhichIsAFolder() {
|
||||
Path filesystemPath = testFilesystem(folder("folderName"));
|
||||
Path filePath = filesystemPath.resolve("folderName").toAbsolutePath();
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
File fileWhichIsAFolder = fileSystem.file("folderName");
|
||||
File target = fileSystem.file("target");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filePath.toString());
|
||||
|
||||
fileWhichIsAFolder.moveTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenWritableDoesNotCreateANonExisitingFile() {
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Path filePath = filesystemPath.resolve("nonExistingFile");
|
||||
File file = NioFileSystem.rootedAt(filesystemPath).file("nonExistingFile");
|
||||
|
||||
file.openWritable();
|
||||
|
||||
assertThat(filePath, doesNotExist());
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.cryptomator.filesystem.nio.integrationtests;
|
||||
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.emptyFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.file;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.testFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.PathMatcher.isDirectory;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.cryptomator.filesystem.nio.NioFileSystem;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
@Ignore
|
||||
public class NioFileSystemIntegrationTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testObtainingAFileSystemWithNonExistingRootCreatesItIncludingAllParentFolders() throws IOException {
|
||||
Path emptyFilesystem = emptyFilesystem();
|
||||
Path nonExistingFilesystemRoot = emptyFilesystem.resolve("nonExistingRoot");
|
||||
Files.delete(emptyFilesystem);
|
||||
|
||||
NioFileSystem.rootedAt(nonExistingFilesystemRoot);
|
||||
|
||||
assertThat(nonExistingFilesystemRoot, isDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObtainingAFileSystemWhooseRootIsAFileFails() throws IOException {
|
||||
Path emptyFilesystem = testFilesystem(file("rootWhichIsAFile"));
|
||||
Path rootWhichIsAFile = emptyFilesystem.resolve("rootWhichIsAFile");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
|
||||
NioFileSystem.rootedAt(rootWhichIsAFile);
|
||||
}
|
||||
}
|
||||
+349
@@ -0,0 +1,349 @@
|
||||
package org.cryptomator.filesystem.nio.integrationtests;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.cryptomator.common.test.matcher.ContainsMatcher.containsInAnyOrder;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.emptyFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.file;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.folder;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.FilesystemSetupUtils.testFilesystem;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.NioNodeMatcher.fileWithName;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.NioNodeMatcher.folderWithName;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.PathMatcher.doesNotExist;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.PathMatcher.isDirectory;
|
||||
import static org.cryptomator.filesystem.nio.integrationtests.PathMatcher.isFile;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.nio.NioFileSystem;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
@Ignore
|
||||
public class NioFolderIntegrationTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testCreateExistingFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path testFilesystemPath = testFilesystem( //
|
||||
folder(folderName));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath);
|
||||
Folder existingFolder = fileSystem.folder(folderName);
|
||||
|
||||
existingFolder.create();
|
||||
|
||||
assertThat(Files.isDirectory(testFilesystemPath.resolve(folderName)), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNonExistingFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path testFilesystemPath = emptyFilesystem();
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath);
|
||||
Folder nonExistingFolder = fileSystem.folder(folderName);
|
||||
|
||||
nonExistingFolder.create();
|
||||
|
||||
assertThat(Files.isDirectory(testFilesystemPath.resolve(folderName)), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFolderWithMissingParent() throws IOException {
|
||||
String parentFolderName = "nameOfParentFolder";
|
||||
String folderName = "nameOfFolder";
|
||||
Path emptyFilesystemPath = emptyFilesystem();
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(emptyFilesystemPath);
|
||||
Folder folderWithNonExistingParent = fileSystem.folder(parentFolderName).folder(folderName);
|
||||
|
||||
folderWithNonExistingParent.create();
|
||||
|
||||
assertThat(Files.isDirectory(emptyFilesystemPath.resolve(parentFolderName).resolve(folderName)), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFolderWhichIsAFile() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path testFilesystemPath = testFilesystem(file(folderName));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(testFilesystemPath);
|
||||
Folder folderWhichIsAFile = fileSystem.folder(folderName);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(testFilesystemPath.resolve(folderName).toString());
|
||||
|
||||
folderWhichIsAFile.create();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenOfEmptyFolder() throws IOException {
|
||||
Folder folder = NioFileSystem.rootedAt(emptyFilesystem());
|
||||
|
||||
assertThat(folder.children().collect(toList()), is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenOfNonExistingFolder() throws IOException {
|
||||
String nameOfNonExistingFolder = "nameOfFolder";
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(nameOfNonExistingFolder);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve(nameOfNonExistingFolder).toString());
|
||||
|
||||
folder.children();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenOfFolderWhichIsAFile() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem(file(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve(folderName).toString());
|
||||
|
||||
folder.children();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildrenOfFolder() throws IOException {
|
||||
String folderName1 = "folder1";
|
||||
String folderName2 = "folder2";
|
||||
String fileName1 = "file1";
|
||||
String fileName2 = "file2";
|
||||
|
||||
Folder folder = NioFileSystem.rootedAt(testFilesystem( //
|
||||
folder(folderName1), //
|
||||
folder(folderName2), //
|
||||
file(fileName1), //
|
||||
file(fileName2)));
|
||||
|
||||
assertThat(folder.children().collect(toList()),
|
||||
containsInAnyOrder( //
|
||||
folderWithName(folderName1), //
|
||||
folderWithName(folderName2), //
|
||||
fileWithName(fileName1), //
|
||||
fileWithName(fileName2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOfEmptyFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
folder.delete();
|
||||
|
||||
assertThat(filesystemPath.resolve(folderName), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOfFolderWithChildren() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder(folderName), //
|
||||
folder(folderName + "/subfolder1"), //
|
||||
file(folderName + "/subfolder1/fileName1"), //
|
||||
folder(folderName + "/subfolder2"), //
|
||||
file(folderName + "/fileName1"), //
|
||||
file(folderName + "/fileName2"));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
folder.delete();
|
||||
|
||||
assertThat(filesystemPath.resolve(folderName), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOfNonExistingFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Folder nonExistingFolder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
nonExistingFolder.delete();
|
||||
|
||||
assertThat(filesystemPath.resolve(folderName), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOfFolderWhichIsAFile() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
folder.delete();
|
||||
|
||||
assertThat(filesystemPath.resolve(folderName), isFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsForExistingFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
assertThat(folder.exists(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsForNonExistingFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Folder nonExistingFolder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
assertThat(nonExistingFolder.exists(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsForFolderWhichIsAFile() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem( //
|
||||
file(folderName));
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
assertThat(folder.exists(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedOfExistingFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Instant lastModified = Instant.parse("2015-12-29T15:36:10.00Z");
|
||||
Folder folder = NioFileSystem
|
||||
.rootedAt(testFilesystem( //
|
||||
folder(folderName).withLastModified(lastModified))) //
|
||||
.folder(folderName);
|
||||
|
||||
assertThat(folder.lastModified(), is(lastModified));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedOfNonExistingFolder() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = emptyFilesystem();
|
||||
Folder folder = NioFileSystem.rootedAt(filesystemPath).folder(folderName);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve(folderName).toString());
|
||||
|
||||
folder.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastModifiedOfFolderWhichIsAFile() throws IOException {
|
||||
String folderName = "nameOfFolder";
|
||||
Path filesystemPath = testFilesystem(file(folderName));
|
||||
Folder folder = NioFileSystem //
|
||||
.rootedAt(filesystemPath) //
|
||||
.folder(folderName);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve(folderName).toString());
|
||||
|
||||
folder.lastModified();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderToNonExistingFolder() throws IOException {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder("folderToMove"));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Folder folderToMove = fileSystem.folder("folderToMove");
|
||||
Folder folderToMoveTo = fileSystem.folder("folderToMoveTo");
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
|
||||
assertThat(filesystemPath.resolve("folderToMove"), doesNotExist());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo"), isDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderWithChildren() throws IOException {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder("folderToMove"), //
|
||||
folder("folderToMove/subfolder1"), //
|
||||
folder("folderToMove/subfolder2"), //
|
||||
file("folderToMove/subfolder1/file1").withData("dataOfFile1"), //
|
||||
file("folderToMove/file2").withData("dataOfFile2"), //
|
||||
file("folderToMove/file3").withData("dataOfFile3"));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Folder folderToMove = fileSystem.folder("folderToMove");
|
||||
Folder folderToMoveTo = fileSystem.folder("folderToMoveTo");
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
|
||||
assertThat(filesystemPath.resolve("folderToMove"), doesNotExist());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/subfolder1"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/subfolder2"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/subfolder1/file1"), isFile().withContent("dataOfFile1"));
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/file2"), isFile().withContent("dataOfFile2"));
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/file3"), isFile().withContent("dataOfFile3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderToExistingFolder() throws IOException {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder("folderToMove"), //
|
||||
file("folderToMove/file1").withData("dataOfFile1"), //
|
||||
file("folderToMove/file2").withData("dataOfFile2"), //
|
||||
folder("folderToMoveTo"), //
|
||||
file("folderToMoveTo/file1").withData("wrongDataOfFile1"), //
|
||||
file("folderToMoveTo/fileWhichShouldNotExistAfterMove"));
|
||||
NioFileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Folder folderToMove = fileSystem.folder("folderToMove");
|
||||
Folder folderToMoveTo = fileSystem.folder("folderToMoveTo");
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
|
||||
assertThat(filesystemPath.resolve("folderToMove"), doesNotExist());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/file1"), isFile().withContent("dataOfFile1"));
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/file2"), isFile().withContent("dataOfFile2"));
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo/fileWhichShouldNotExistAfterMove"), doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderToExistingFile() throws IOException {
|
||||
Path filesystemPath = testFilesystem( //
|
||||
folder("folderToMove"), //
|
||||
file("folderToMoveTo"));
|
||||
FileSystem fileSystem = NioFileSystem.rootedAt(filesystemPath);
|
||||
Folder folderToMove = fileSystem.folder("folderToMove");
|
||||
Folder folderToMoveTo = fileSystem.folder("folderToMoveTo");
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectMessage(filesystemPath.resolve("folderToMoveTo").toString());
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToOfFolderToFolderOfAnotherFileSystem() throws IOException {
|
||||
Path filesystemPath = testFilesystem(folder("folderToMove"));
|
||||
Folder folderToMove = NioFileSystem.rootedAt(filesystemPath).folder("folderToMove");
|
||||
Folder folderToMoveTo = NioFileSystem.rootedAt(filesystemPath).folder("folderToMoveTo");
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
|
||||
folderToMove.moveTo(folderToMoveTo);
|
||||
|
||||
assertThat(filesystemPath.resolve("folderToMove"), isDirectory());
|
||||
assertThat(filesystemPath.resolve("folderToMoveTo"), doesNotExist());
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
package org.cryptomator.filesystem.nio.integrationtests;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
||||
+27
-1
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
package org.cryptomator.filesystem.nio.integrationtests;
|
||||
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -63,6 +64,10 @@ class PathMatcher {
|
||||
return new IsFileWithContentMatcher(value.getBytes());
|
||||
}
|
||||
|
||||
public Matcher<Path> withPartialContentAt(int offset, byte[] value) {
|
||||
return new IsFileWithContentMatcher(offset, value);
|
||||
}
|
||||
|
||||
public Matcher<Path> withContent(byte[] value) {
|
||||
return new IsFileWithContentMatcher(value);
|
||||
}
|
||||
@@ -75,10 +80,19 @@ class PathMatcher {
|
||||
|
||||
public static class IsFileWithContentMatcher extends TypeSafeDiagnosingMatcher<Path> {
|
||||
|
||||
private static final int NO_OFFSET = -1;
|
||||
|
||||
private final byte[] expectedContent;
|
||||
private final int offset;
|
||||
|
||||
public IsFileWithContentMatcher(int offset, byte[] content) {
|
||||
this.expectedContent = content;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public IsFileWithContentMatcher(byte[] content) {
|
||||
this.expectedContent = content;
|
||||
this.offset = NO_OFFSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,6 +100,10 @@ class PathMatcher {
|
||||
description //
|
||||
.appendText("a file with content ") //
|
||||
.appendText(Arrays.toString(expectedContent));
|
||||
if (offset != NO_OFFSET) {
|
||||
description.appendText(" starting at byte ") //
|
||||
.appendValue(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,10 +114,18 @@ class PathMatcher {
|
||||
}
|
||||
|
||||
byte[] actualContent = getFileContent(path);
|
||||
if (offset != NO_OFFSET) {
|
||||
actualContent = copyOfRange(actualContent, offset, offset + expectedContent.length);
|
||||
}
|
||||
|
||||
if (!Arrays.equals(actualContent, expectedContent)) {
|
||||
mismatchDescription //
|
||||
.appendText("a file with content ") //
|
||||
.appendText(Arrays.toString(actualContent));
|
||||
if (offset != NO_OFFSET) {
|
||||
mismatchDescription.appendText(" starting at byte ") //
|
||||
.appendValue(offset);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user