From 8ff565968029a43c9685f441a0b65ff6b0942463 Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Sat, 23 Jan 2016 00:01:27 +0100 Subject: [PATCH] Using AsynchronousFileChannel * Replaced FileChannel with AsynchronousFileChannel * Adapted tests ** transferTo tests and exception handling tests still pending (see TODOs) --- .../invariants/FolderCopyToTests.java | 3 - .../filesystem/nio/DefaultNioAccess.java | 8 +- .../cryptomator/filesystem/nio/NioAccess.java | 6 +- .../filesystem/nio/SharedFileChannel.java | 45 ++- .../filesystem/nio/DefaultNioAccessTest.java | 18 +- .../filesystem/nio/FileChannelAdapter.java | 96 ------ .../filesystem/nio/SharedFileChannelTest.java | 315 +++++++++--------- 7 files changed, 205 insertions(+), 286 deletions(-) delete mode 100644 main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/FileChannelAdapter.java diff --git a/main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/FolderCopyToTests.java b/main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/FolderCopyToTests.java index cf71b4c59..2fc117f20 100644 --- a/main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/FolderCopyToTests.java +++ b/main/filesystem-invariants-tests/src/test/java/org/cryptomator/filesystem/invariants/FolderCopyToTests.java @@ -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) { diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/DefaultNioAccess.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/DefaultNioAccess.java index 726e4a7f6..5d61ccb89 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/DefaultNioAccess.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/DefaultNioAccess.java @@ -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(); } diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioAccess.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioAccess.java index 48a61973d..cb75026f4 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioAccess.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioAccess.java @@ -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 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; diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/SharedFileChannel.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/SharedFileChannel.java index be89284e6..9d3b08da9 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/SharedFileChannel.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/SharedFileChannel.java @@ -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; } diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/DefaultNioAccessTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/DefaultNioAccessTest.java index 4e98349d2..3b767efbd 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/DefaultNioAccessTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/DefaultNioAccessTest.java @@ -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 diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/FileChannelAdapter.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/FileChannelAdapter.java deleted file mode 100644 index 095685ab9..000000000 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/FileChannelAdapter.java +++ /dev/null @@ -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 { - } - -} diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/SharedFileChannelTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/SharedFileChannelTest.java index 49c173ba1..47aca6d6b 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/SharedFileChannelTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/SharedFileChannelTest.java @@ -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 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() { + when(channel.read(buffer, 0)).thenAnswer(new Answer>() { @Override - public Integer answer(InvocationOnMock invocation) throws Throwable { + public Future 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 simulateRead(int amount, ByteBuffer target) { - return new Answer() { + private Answer> simulateRead(int amount, ByteBuffer target) { + return new Answer>() { @Override - public Integer answer(InvocationOnMock invocation) throws Throwable { + public Future 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 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 simulateWrite(int amount, ByteBuffer target) { - return new Answer() { + private Answer> simulateWrite(int amount, ByteBuffer target) { + return new Answer>() { @Override - public Integer answer(InvocationOnMock invocation) throws Throwable { + public Future answer(InvocationOnMock invocation) throws Throwable { target.position(target.position() + amount); - return amount; + return constantFuture(amount); } }; }