mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-07-19 14:32:23 +00:00
Using AsynchronousFileChannel
* Replaced FileChannel with AsynchronousFileChannel * Adapted tests ** transferTo tests and exception handling tests still pending (see TODOs)
This commit is contained in:
-3
@@ -13,7 +13,6 @@ import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.invariants.FileSystemFactories.FileSystemFactory;
|
||||
import org.cryptomator.filesystem.invariants.WaysToObtainAFile.WayToObtainAFile;
|
||||
import org.cryptomator.filesystem.invariants.WaysToObtainAFolder.WayToObtainAFolder;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.experimental.theories.DataPoints;
|
||||
import org.junit.experimental.theories.Theories;
|
||||
@@ -104,8 +103,6 @@ public class FolderCopyToTests {
|
||||
}
|
||||
|
||||
@Theory
|
||||
@Ignore
|
||||
// FIXME not working yet due to concurrent access to and interruption of channel
|
||||
public void testCopyAFolderWithChildrenCopiesChildrenRecursive(FileSystemFactory fileSystemFactory, WayToObtainAFolder wayToObtainAnExistingFolder, WayToObtainAFile wayToObtainAnExisitingFile,
|
||||
WayToObtainAFolder wayToObtainANonExistingFolder) {
|
||||
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.AsynchronousFileChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
@@ -17,8 +17,8 @@ import java.util.stream.Stream;
|
||||
class DefaultNioAccess implements NioAccess {
|
||||
|
||||
@Override
|
||||
public FileChannel open(Path path, OpenOption... options) throws IOException {
|
||||
return FileChannel.open(path, options);
|
||||
public AsynchronousFileChannel open(Path path, OpenOption... options) throws IOException {
|
||||
return AsynchronousFileChannel.open(path, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,7 +57,7 @@ class DefaultNioAccess implements NioAccess {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(FileChannel channel) throws IOException {
|
||||
public void close(AsynchronousFileChannel channel) throws IOException {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.AsynchronousFileChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
@@ -16,7 +16,7 @@ interface NioAccess {
|
||||
|
||||
public static final Holder<NioAccess> DEFAULT = new Holder<>(new DefaultNioAccess());
|
||||
|
||||
FileChannel open(Path path, OpenOption... options) throws IOException;
|
||||
AsynchronousFileChannel open(Path path, OpenOption... options) throws IOException;
|
||||
|
||||
boolean isRegularFile(Path path, LinkOption... options);
|
||||
|
||||
@@ -32,7 +32,7 @@ interface NioAccess {
|
||||
|
||||
void delete(Path path) throws IOException;
|
||||
|
||||
void close(FileChannel channel) throws IOException;
|
||||
void close(AsynchronousFileChannel channel) throws IOException;
|
||||
|
||||
void move(Path source, Path target, CopyOption... options) throws IOException;
|
||||
|
||||
|
||||
+31
-14
@@ -3,12 +3,14 @@ package org.cryptomator.filesystem.nio;
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.AsynchronousFileChannel;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@@ -22,7 +24,7 @@ class SharedFileChannel {
|
||||
|
||||
private Lock lock = new ReentrantLock();
|
||||
|
||||
private FileChannel delegate;
|
||||
private AsynchronousFileChannel delegate;
|
||||
|
||||
public SharedFileChannel(Path path, NioAccess nioAccess) {
|
||||
this.path = path;
|
||||
@@ -92,16 +94,18 @@ class SharedFileChannel {
|
||||
assertOpen();
|
||||
try {
|
||||
return tryReadFully(position, target);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} catch (InterruptedException e) {
|
||||
throw new UncheckedIOException(new InterruptedIOException("read has been interrupted"));
|
||||
} catch (ExecutionException e) {
|
||||
throw new UncheckedIOException(new IOException(e));
|
||||
}
|
||||
}
|
||||
|
||||
private int tryReadFully(long position, ByteBuffer target) throws IOException {
|
||||
private int tryReadFully(long position, ByteBuffer target) throws InterruptedException, ExecutionException {
|
||||
int initialRemaining = target.remaining();
|
||||
long maxPosition = position + initialRemaining;
|
||||
do {
|
||||
if (delegate.read(target, maxPosition - target.remaining()) == EOF) {
|
||||
if (delegate.read(target, maxPosition - target.remaining()).get() == EOF) {
|
||||
if (initialRemaining == target.remaining()) {
|
||||
return EOF;
|
||||
} else {
|
||||
@@ -137,14 +141,25 @@ class SharedFileChannel {
|
||||
throw new IllegalArgumentException("Count must not be negative");
|
||||
}
|
||||
try {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(32 * 1024);
|
||||
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);
|
||||
long transferred = 0;
|
||||
while (transferred < transferCount) {
|
||||
int read = delegate.read(buffer, position + transferred).get();
|
||||
if (read == -1) {
|
||||
throw new IllegalStateException("Reached end of file during transfer to");
|
||||
}
|
||||
buffer.flip();
|
||||
while (buffer.hasRemaining()) {
|
||||
transferred += targetChannel.delegate.write(buffer, targetPosition + transferred).get();
|
||||
}
|
||||
}
|
||||
return transferCount;
|
||||
} catch (InterruptedException e) {
|
||||
throw new UncheckedIOException(new InterruptedIOException("read has been interrupted"));
|
||||
} catch (ExecutionException e) {
|
||||
throw new UncheckedIOException(new IOException(e));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
@@ -163,16 +178,18 @@ class SharedFileChannel {
|
||||
assertOpen();
|
||||
try {
|
||||
return tryWriteFully(position, source);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} catch (InterruptedException e) {
|
||||
throw new UncheckedIOException(new InterruptedIOException("read has been interrupted"));
|
||||
} catch (ExecutionException e) {
|
||||
throw new UncheckedIOException(new IOException(e));
|
||||
}
|
||||
}
|
||||
|
||||
private int tryWriteFully(long position, ByteBuffer source) throws IOException {
|
||||
private int tryWriteFully(long position, ByteBuffer source) throws InterruptedException, ExecutionException {
|
||||
int count = source.remaining();
|
||||
long maxPosition = position + count;
|
||||
do {
|
||||
delegate.write(source, maxPosition - source.remaining());
|
||||
delegate.write(source, maxPosition - source.remaining()).get();
|
||||
} while (source.hasRemaining());
|
||||
return count;
|
||||
}
|
||||
|
||||
+6
-12
@@ -15,7 +15,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.AsynchronousFileChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
@@ -64,10 +64,10 @@ public class DefaultNioAccessTest {
|
||||
@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);
|
||||
AsynchronousFileChannel channel = mock(AsynchronousFileChannel.class);
|
||||
when(provider.newAsynchronousFileChannel(path, new HashSet<>(asList(options)), null, new FileAttribute[0])).thenReturn(channel);
|
||||
|
||||
FileChannel result = inTest.open(path, options);
|
||||
AsynchronousFileChannel result = inTest.open(path, options);
|
||||
|
||||
assertThat(result, is(channel));
|
||||
}
|
||||
@@ -177,17 +177,11 @@ public class DefaultNioAccessTest {
|
||||
|
||||
@Test
|
||||
public void testCloseInvokesCloseOnChannel() throws IOException {
|
||||
Runnable implCloseChannel = mock(Runnable.class);
|
||||
FileChannel channel = new FileChannelAdapter() {
|
||||
@Override
|
||||
public void implCloseChannel() throws IOException {
|
||||
implCloseChannel.run();
|
||||
}
|
||||
};
|
||||
AsynchronousFileChannel channel = mock(AsynchronousFileChannel.class);
|
||||
|
||||
inTest.close(channel);
|
||||
|
||||
verify(implCloseChannel).run();
|
||||
verify(channel).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
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 {
|
||||
}
|
||||
|
||||
}
|
||||
+161
-154
@@ -1,11 +1,11 @@
|
||||
package org.cryptomator.filesystem.nio;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.apache.commons.lang3.concurrent.ConcurrentUtils.constantFuture;
|
||||
import static org.cryptomator.filesystem.nio.SharedFileChannel.EOF;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
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;
|
||||
@@ -17,9 +17,11 @@ import static org.mockito.Mockito.when;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.AsynchronousFileChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.cryptomator.common.Holder;
|
||||
import org.junit.Before;
|
||||
@@ -122,7 +124,7 @@ public class SharedFileChannelTest {
|
||||
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));
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(mock(AsynchronousFileChannel.class));
|
||||
|
||||
inThreadRethrowingException(() -> inTest.open(OpenMode.WRITE));
|
||||
inThreadRethrowingException(() -> inTest.open(OpenMode.WRITE));
|
||||
@@ -146,7 +148,7 @@ public class SharedFileChannelTest {
|
||||
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));
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(mock(AsynchronousFileChannel.class));
|
||||
inTest.open(OpenMode.WRITE);
|
||||
inTest.close();
|
||||
|
||||
@@ -160,7 +162,7 @@ public class SharedFileChannelTest {
|
||||
public void testCloseForcesAndClosesChannel() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
AsynchronousFileChannel channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
|
||||
@@ -175,7 +177,7 @@ public class SharedFileChannelTest {
|
||||
public void testCloseWrapsIOExceptionFromForceInUncheckedIOExceptionAndStillClosesChannel() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
AsynchronousFileChannel channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
IOException exceptionFromForce = new IOException();
|
||||
@@ -195,7 +197,7 @@ public class SharedFileChannelTest {
|
||||
public void testCloseWrapsIOExceptionFromCloseInUncheckedIOException() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
AsynchronousFileChannel channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
IOException exceptionFromClose = new IOException();
|
||||
@@ -211,7 +213,7 @@ public class SharedFileChannelTest {
|
||||
public void testCloseDoesNotCloseChannelIfOpenedTwice() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
AsynchronousFileChannel channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
inThreadRethrowingException(() -> inTest.open(OpenMode.WRITE));
|
||||
@@ -225,7 +227,7 @@ public class SharedFileChannelTest {
|
||||
public void testLastCloseDoesCloseChannelIfOpenedTwice() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(false);
|
||||
FileChannel channel = mock(FileChannel.class);
|
||||
AsynchronousFileChannel channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
inThreadRethrowingException(() -> {
|
||||
@@ -245,25 +247,27 @@ public class SharedFileChannelTest {
|
||||
@Rule
|
||||
public Timeout timeoutRule = Timeout.seconds(1);
|
||||
|
||||
private FileChannel channel;
|
||||
private AsynchronousFileChannel channel;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFullyWrapsExceptionFromReadInUncheckedIOException() throws IOException {
|
||||
public void testReadFullyWrapsExceptionFromReadInUncheckedIOException() throws InterruptedException, ExecutionException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(0);
|
||||
IOException exceptionFromRead = new IOException();
|
||||
when(channel.read(buffer, 0)).thenThrow(exceptionFromRead);
|
||||
ExecutionException exceptionFromRead = new ExecutionException(new IOException());
|
||||
Future<Integer> result = mock(Future.class);
|
||||
when(channel.read(buffer, 0)).thenReturn(result);
|
||||
when(result.get()).thenThrow(exceptionFromRead);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromRead));
|
||||
thrown.expectCause(is(instanceOf(IOException.class))); // TODO check correct cause of cause
|
||||
|
||||
inTest.readFully(0, buffer);
|
||||
}
|
||||
@@ -271,11 +275,11 @@ public class SharedFileChannelTest {
|
||||
@Test
|
||||
public void testReadFullyDelegatesToChannelRead() throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(50);
|
||||
when(channel.read(buffer, 0)).thenAnswer(new Answer<Integer>() {
|
||||
when(channel.read(buffer, 0)).thenAnswer(new Answer<Future<Integer>>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
public Future<Integer> answer(InvocationOnMock invocation) throws Throwable {
|
||||
buffer.position(50);
|
||||
return 50;
|
||||
return constantFuture(50);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -289,7 +293,7 @@ public class SharedFileChannelTest {
|
||||
@Test
|
||||
public void testReadFullyReturnsEofWhenFirstReadReturnsIt() throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(50);
|
||||
when(channel.read(buffer, 0)).thenReturn(EOF);
|
||||
when(channel.read(buffer, 0)).thenReturn(constantFuture(EOF));
|
||||
|
||||
int result = inTest.readFully(0, buffer);
|
||||
|
||||
@@ -302,7 +306,7 @@ public class SharedFileChannelTest {
|
||||
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);
|
||||
when(channel.read(buffer, 20)).thenReturn(constantFuture(EOF));
|
||||
|
||||
int result = inTest.readFully(0, buffer);
|
||||
|
||||
@@ -328,12 +332,12 @@ public class SharedFileChannelTest {
|
||||
verifyNoMoreInteractions(channel);
|
||||
}
|
||||
|
||||
private Answer<Integer> simulateRead(int amount, ByteBuffer target) {
|
||||
return new Answer<Integer>() {
|
||||
private Answer<Future<Integer>> simulateRead(int amount, ByteBuffer target) {
|
||||
return new Answer<Future<Integer>>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
public Future<Integer> answer(InvocationOnMock invocation) throws Throwable {
|
||||
target.position(target.position() + amount);
|
||||
return amount;
|
||||
return constantFuture(amount);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -342,13 +346,13 @@ public class SharedFileChannelTest {
|
||||
|
||||
public class Truncate {
|
||||
|
||||
private FileChannel channel;
|
||||
private AsynchronousFileChannel channel;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
@@ -378,13 +382,13 @@ public class SharedFileChannelTest {
|
||||
|
||||
public class Size {
|
||||
|
||||
private FileChannel channel;
|
||||
private AsynchronousFileChannel channel;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
@@ -412,148 +416,151 @@ public class SharedFileChannelTest {
|
||||
|
||||
}
|
||||
|
||||
public class TransferTo {
|
||||
|
||||
private FileChannel channel;
|
||||
|
||||
private Path targetPath;
|
||||
private SharedFileChannel targetInTest;
|
||||
private FileChannel targetChannel;
|
||||
|
||||
@Before
|
||||
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 testTransferToThrowsIllegalArugmentExceptionIfCountIsNegative() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Count must not be negative");
|
||||
|
||||
inTest.transferTo(0, -1, targetInTest, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
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 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 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 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 testTransferToWrapsIOExceptionFromTransferToInUncheckedIOException() throws IOException {
|
||||
when(channel.size()).thenReturn(Long.MAX_VALUE);
|
||||
IOException exceptionFromTransferTo = new IOException();
|
||||
when(channel.transferTo(anyLong(), anyLong(), any())).thenThrow(exceptionFromTransferTo);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromTransferTo));
|
||||
|
||||
inTest.transferTo(0L, 10L, targetInTest, 0L);
|
||||
}
|
||||
|
||||
}
|
||||
// TODO fix / implement tests
|
||||
// public class TransferTo {
|
||||
//
|
||||
// private AsynchronousFileChannel channel;
|
||||
//
|
||||
// private Path targetPath;
|
||||
// private SharedFileChannel targetInTest;
|
||||
// private AsynchronousFileChannel targetChannel;
|
||||
//
|
||||
// @Before
|
||||
// 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(AsynchronousFileChannel.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(AsynchronousFileChannel.class);
|
||||
// when(nioAccess.open(targetPath, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(targetChannel);
|
||||
// targetInTest.open(OpenMode.WRITE);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testTransferToThrowsIllegalArugmentExceptionIfCountIsNegative() {
|
||||
// thrown.expect(IllegalArgumentException.class);
|
||||
// thrown.expectMessage("Count must not be negative");
|
||||
//
|
||||
// inTest.transferTo(0, -1, targetInTest, 0);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// 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 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 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 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 testTransferToWrapsIOExceptionFromTransferToInUncheckedIOException() throws IOException {
|
||||
// when(channel.size()).thenReturn(Long.MAX_VALUE);
|
||||
// IOException exceptionFromTransferTo = new IOException();
|
||||
// when(channel.transferTo(anyLong(), anyLong(), any())).thenThrow(exceptionFromTransferTo);
|
||||
//
|
||||
// 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;
|
||||
private AsynchronousFileChannel channel;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
when(nioAccess.isDirectory(path)).thenReturn(false);
|
||||
when(nioAccess.isRegularFile(path)).thenReturn(true);
|
||||
channel = mock(FileChannel.class);
|
||||
channel = mock(AsynchronousFileChannel.class);
|
||||
when(nioAccess.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)).thenReturn(channel);
|
||||
inTest.open(OpenMode.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFullyWrapsIOExceptionFromWriteIntoUncheckedIOException() throws IOException {
|
||||
public void testWriteFullyWrapsIOExceptionFromWriteIntoUncheckedIOException() throws InterruptedException, ExecutionException {
|
||||
int count = 1;
|
||||
int position = 0;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(count);
|
||||
IOException exceptionFromWrite = new IOException();
|
||||
when(channel.write(buffer, position)).thenThrow(exceptionFromWrite);
|
||||
ExecutionException exceptionFromWrite = new ExecutionException(new IOException());
|
||||
Future<Integer> result = mock(Future.class);
|
||||
when(channel.write(buffer, position)).thenReturn(result);
|
||||
when(result.get()).thenThrow(exceptionFromWrite);
|
||||
|
||||
thrown.expect(UncheckedIOException.class);
|
||||
thrown.expectCause(is(exceptionFromWrite));
|
||||
thrown.expectCause(is(instanceOf(IOException.class))); // TODO check correct cause of cause
|
||||
|
||||
inTest.writeFully(position, buffer);
|
||||
}
|
||||
@@ -604,12 +611,12 @@ public class SharedFileChannelTest {
|
||||
verify(channel).write(buffer, position);
|
||||
}
|
||||
|
||||
private Answer<Integer> simulateWrite(int amount, ByteBuffer target) {
|
||||
return new Answer<Integer>() {
|
||||
private Answer<Future<Integer>> simulateWrite(int amount, ByteBuffer target) {
|
||||
return new Answer<Future<Integer>>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
public Future<Integer> answer(InvocationOnMock invocation) throws Throwable {
|
||||
target.position(target.position() + amount);
|
||||
return amount;
|
||||
return constantFuture(amount);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user