From 71face80918da435fb7017a39f22307e87cc5f2b Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Fri, 1 Jan 2016 19:00:31 +0100 Subject: [PATCH] Tests for filesystem-nio * Renamed existing tests to ...IntegrationTest * Created Unit-Tests for Readable- and WritableNioFile (incomplete) --- .../cryptomator/filesystem/nio/NioFile.java | 7 +- .../filesystem/nio/ReadableNioFile.java | 4 + .../filesystem/nio/WritableNioFile.java | 6 +- ...eTest.java => NioFileIntegrationTest.java} | 2 +- ...java => NioFileSystemIntegrationTest.java} | 2 +- ...est.java => NioFolderIntegrationTest.java} | 2 +- .../filesystem/nio/ReadableNioFileTest.java | 313 ++++++++++++++++++ .../filesystem/nio/WritableNioFileTest.java | 169 ++++++++++ 8 files changed, 498 insertions(+), 7 deletions(-) rename main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/{NioFileTest.java => NioFileIntegrationTest.java} (99%) rename main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/{NioFileSystemTest.java => NioFileSystemIntegrationTest.java} (97%) rename main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/{NioFolderTest.java => NioFolderIntegrationTest.java} (99%) create mode 100644 main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/ReadableNioFileTest.java create mode 100644 main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WritableNioFileTest.java diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java index 2df9931d5..2b3342455 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java @@ -8,6 +8,7 @@ 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; @@ -28,7 +29,7 @@ class NioFile extends NioNode implements File { return sharedChannel; } - public ReentrantReadWriteLock lock() { + public ReadWriteLock lock() { return lock; } @@ -84,4 +85,8 @@ class NioFile extends NioNode implements File { return format("NioFile(%s)", path); } + Path path() { + return path; + } + } diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/ReadableNioFile.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/ReadableNioFile.java index ce6ee910f..64f5b596f 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/ReadableNioFile.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/ReadableNioFile.java @@ -40,6 +40,9 @@ class ReadableNioFile implements ReadableFile { @Override public void position(long position) throws UncheckedIOException { assertOpen(); + if (position < 0) { + throw new IllegalArgumentException(); + } this.position = position; } @@ -58,6 +61,7 @@ class ReadableNioFile implements ReadableFile { } private void internalCopyTo(WritableNioFile target) { + target.assertOpen(); target.ensureChannelIsOpened(); SharedFileChannel targetChannel = target.channel(); targetChannel.truncate(0); diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/WritableNioFile.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/WritableNioFile.java index 0347c1470..4826e10ef 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/WritableNioFile.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/WritableNioFile.java @@ -105,7 +105,7 @@ class WritableNioFile implements WritableFile { assertOpen(); try { closeChannelIfOpened(); - Files.delete(nioFile.path); + Files.delete(nioFile.path()); } catch (IOException e) { throw new UncheckedIOException(e); } finally { @@ -155,11 +155,11 @@ class WritableNioFile implements WritableFile { return nioFile.path; } - public NioFile nioFile() { + NioFile nioFile() { return nioFile; } - private void assertOpen() { + void assertOpen() { if (!open) { throw new UncheckedIOException(format("%s already closed.", this), new ClosedChannelException()); } diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileIntegrationTest.java similarity index 99% rename from main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileTest.java rename to main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileIntegrationTest.java index 7f6212781..84b8a2c5f 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileIntegrationTest.java @@ -28,7 +28,7 @@ import org.junit.runner.RunWith; import de.bechte.junit.runners.context.HierarchicalContextRunner; @RunWith(HierarchicalContextRunner.class) -public class NioFileTest { +public class NioFileIntegrationTest { @Rule public ExpectedException thrown = ExpectedException.none(); diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileSystemTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileSystemIntegrationTest.java similarity index 97% rename from main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileSystemTest.java rename to main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileSystemIntegrationTest.java index 6d151fd37..bfa22ddb8 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileSystemTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileSystemIntegrationTest.java @@ -16,7 +16,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -public class NioFileSystemTest { +public class NioFileSystemIntegrationTest { @Rule public ExpectedException thrown = ExpectedException.none(); diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderIntegrationTest.java similarity index 99% rename from main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java rename to main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderIntegrationTest.java index 3ef5acedf..eb00842f0 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderIntegrationTest.java @@ -31,7 +31,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -public class NioFolderTest { +public class NioFolderIntegrationTest { @Rule public ExpectedException thrown = ExpectedException.none(); diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/ReadableNioFileTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/ReadableNioFileTest.java new file mode 100644 index 000000000..af8f81fb2 --- /dev/null +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/ReadableNioFileTest.java @@ -0,0 +1,313 @@ +package org.cryptomator.filesystem.nio; + +import static org.cryptomator.filesystem.nio.OpenMode.READ; +import static org.hamcrest.CoreMatchers.is; +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.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +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 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 SharedFileChannel channel; + + private ReadWriteLock lock; + + private Lock readLock; + + @Before + public void setup() { + file = mock(NioFile.class); + channel = mock(SharedFileChannel.class); + lock = mock(ReadWriteLock.class); + readLock = mock(Lock.class); + + when(file.channel()).thenReturn(channel); + when(file.lock()).thenReturn(lock); + when(lock.readLock()).thenReturn(readLock); + } + + @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(); + + thrown.expect(UncheckedIOException.class); + thrown.expectMessage("already closed"); + + inTest.read(irrelevant); + } + + @Test + public void testPositionFailsIfClosed() { + ReadableNioFile inTest = new ReadableNioFile(file); + int irrelevant = 1; + inTest.close(); + + thrown.expect(UncheckedIOException.class); + thrown.expectMessage("already closed"); + + inTest.position(irrelevant); + } + + @Test + public void testPositionFailsIfNegative() { + ReadableNioFile inTest = new ReadableNioFile(file); + + thrown.expect(IllegalArgumentException.class); + + inTest.position(-1); + } + + @Test + public void testReadDelegatesToChannelReadFullyWithZeroPositionIfNotSet() { + ReadableNioFile inTest = new ReadableNioFile(file); + ByteBuffer buffer = mock(ByteBuffer.class); + + inTest.read(buffer); + + verify(channel).readFully(0, buffer); + } + + @Test + public void testReadDelegatesToChannelReadFullyWithPositionAtEndOfPreviousReadIfInvokedTwice() { + ReadableNioFile inTest = new ReadableNioFile(file); + ByteBuffer buffer = mock(ByteBuffer.class); + int endOfPreviousRead = 10; + when(channel.readFully(0, buffer)).thenReturn(endOfPreviousRead); + + inTest.read(buffer); + inTest.read(buffer); + + verify(channel).readFully(0, buffer); + verify(channel).readFully(10, buffer); + } + + @Test + public void testReadDelegatesToChannelReadFullyWithPositionUnchangedIfPreviousReadReturnedEof() { + ReadableNioFile inTest = new ReadableNioFile(file); + ByteBuffer buffer = mock(ByteBuffer.class); + when(channel.readFully(0, buffer)).thenReturn(SharedFileChannel.EOF); + + inTest.read(buffer); + inTest.read(buffer); + + verify(channel, times(2)).readFully(0, buffer); + } + + @Test + public void testReadDelegatesToChannelReadFullyWithSetPosition() { + ReadableNioFile inTest = new ReadableNioFile(file); + ByteBuffer buffer = mock(ByteBuffer.class); + int position = 10; + inTest.position(position); + + inTest.read(buffer); + + verify(channel).readFully(position, buffer); + } + + @Test + public void testReadReturnsValueOfChannelReadFully() { + ReadableNioFile inTest = new ReadableNioFile(file); + ByteBuffer buffer = mock(ByteBuffer.class); + int expectedResult = 37028; + when(channel.readFully(0, buffer)).thenReturn(expectedResult); + + int result = inTest.read(buffer); + + assertThat(result, is(expectedResult)); + } + + @Test + public void testReadDoesNotModifyBuffer() { + ReadableNioFile inTest = new ReadableNioFile(file); + ByteBuffer buffer = mock(ByteBuffer.class); + + inTest.read(buffer); + + verifyZeroInteractions(buffer); + } + + public class CopyTo { + + @Mock + private NioFile otherFile; + + @Mock + private WritableNioFile writableOtherFile; + + @Mock + private SharedFileChannel otherChannel; + + @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); + } + + @Test + public void testCopyToFailsIfTargetBelongsToOtherFileSystem() { + ReadableNioFile inTest = new ReadableNioFile(file); + when(otherFile.belongsToSameFilesystem(file)).thenReturn(false); + + thrown.expect(IllegalArgumentException.class); + + inTest.copyTo(writableOtherFile); + } + + @Test + public void testCopyToFailsIfSourceIsClosed() { + ReadableNioFile inTest = new ReadableNioFile(file); + when(otherFile.belongsToSameFilesystem(file)).thenReturn(true); + inTest.close(); + + thrown.expect(UncheckedIOException.class); + thrown.expectMessage("already closed"); + + inTest.copyTo(writableOtherFile); + } + + @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)).thenReturn(sizeOfSourceChannel); + + inTest.copyTo(writableOtherFile); + + 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); + } + + @Test + public void testCopyToInvokesTransferToUntilAllBytesHaveBeenTransferred() { + ReadableNioFile inTest = new ReadableNioFile(file); + when(otherFile.belongsToSameFilesystem(file)).thenReturn(true); + long firstTransferAmount = 100; + long secondTransferAmount = 300; + long thirdTransferAmount = 500; + long sizeRemainingAfterSecondTransfer = thirdTransferAmount; + long sizeRemainingAfterFirstTransfer = sizeRemainingAfterSecondTransfer + secondTransferAmount; + long size = sizeRemainingAfterFirstTransfer + firstTransferAmount; + when(channel.size()).thenReturn(size); + when(channel.transferTo(0, size, otherChannel)).thenReturn(firstTransferAmount); + when(channel.transferTo(firstTransferAmount, sizeRemainingAfterFirstTransfer, otherChannel)).thenReturn(secondTransferAmount); + when(channel.transferTo(firstTransferAmount + secondTransferAmount, sizeRemainingAfterSecondTransfer, otherChannel)).thenReturn(thirdTransferAmount); + + inTest.copyTo(writableOtherFile); + + InOrder inOrder = inOrder(channel); + inOrder.verify(channel).transferTo(0, size, otherChannel); + inOrder.verify(channel).transferTo(firstTransferAmount, sizeRemainingAfterFirstTransfer, otherChannel); + inOrder.verify(channel).transferTo(firstTransferAmount + secondTransferAmount, sizeRemainingAfterSecondTransfer, otherChannel); + } + + } + + @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)); + } + + @Test + public void testCloseClosesChannelAndUnlocksReadLock() { + ReadableNioFile inTest = new ReadableNioFile(file); + + inTest.close(); + + InOrder inOrder = Mockito.inOrder(channel, readLock); + inOrder.verify(channel).close(); + inOrder.verify(readLock).unlock(); + } + + @Test + public void testCloseClosesChannelAndUnlocksReadLockOnlyOnceIfInvokedTwice() { + ReadableNioFile inTest = new ReadableNioFile(file); + + inTest.close(); + inTest.close(); + + InOrder inOrder = Mockito.inOrder(channel, readLock); + inOrder.verify(channel).close(); + inOrder.verify(readLock).unlock(); + } + + @Test + public void testCloseUnlocksReadLockEvenIfCloseFails() { + ReadableNioFile inTest = new ReadableNioFile(file); + String message = "exceptionMessage"; + doThrow(new RuntimeException(message)).when(channel).close(); + + thrown.expectMessage(message); + + try { + inTest.close(); + } finally { + verify(readLock).unlock(); + } + } + + @Test + public void testToString() { + String nioFileToString = file.toString(); + ReadableNioFile inTest = new ReadableNioFile(file); + + assertThat(inTest.toString(), is("Readable" + nioFileToString)); + } + +} diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WritableNioFileTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WritableNioFileTest.java new file mode 100644 index 000000000..023ac41b5 --- /dev/null +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WritableNioFileTest.java @@ -0,0 +1,169 @@ +package org.cryptomator.filesystem.nio; + +import static org.cryptomator.filesystem.nio.OpenMode.WRITE; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +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 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 de.bechte.junit.runners.context.HierarchicalContextRunner; + +@RunWith(HierarchicalContextRunner.class) +@SuppressWarnings("resource") +public class WritableNioFileTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private NioFile file; + + private SharedFileChannel channel; + + private ReadWriteLock lock; + + private Lock writeLock; + + @Before + public void setup() { + file = mock(NioFile.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); + } + + @Test + public void testWriteFailsIfClosed() { + WritableNioFile inTest = new WritableNioFile(file); + inTest.close(); + ByteBuffer irrelevant = null; + + 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 testToString() { + String fileToString = file.toString(); + WritableNioFile inTest = new WritableNioFile(file); + + assertThat(inTest.toString(), is("Writable" + fileToString)); + } + +}