Tests for filesystem-nio

* Renamed tests from ...IntegrationTest back to ...Test
** to allow better integration with moreunit
** because some methods of the classes can only be integration tested
some not which lead to a strange splitting of the tests
* Added more tests
This commit is contained in:
Markus Kreusch
2016-01-02 02:05:13 +01:00
parent e9f5593e33
commit ff4448bac0
10 changed files with 816 additions and 18 deletions

View File

@@ -68,7 +68,7 @@ class ReadableNioFile implements ReadableFile {
long size = nioFile.channel().size();
long transferred = 0;
while (transferred < size) {
transferred += nioFile.channel().transferTo(transferred, size - transferred, targetChannel);
transferred += nioFile.channel().transferTo(transferred, size - transferred, targetChannel, transferred);
}
}

View File

@@ -42,12 +42,26 @@ class SharedFileChannel {
assertOpenedByCurrentThread();
doLocked(() -> {
openedBy.remove(Thread.currentThread());
if (openedBy.isEmpty()) {
closeChannel();
try {
delegate.force(true);
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
if (openedBy.isEmpty()) {
closeChannel();
}
}
});
}
/**
* @deprecated only intended to be used in tests
*/
@Deprecated
void forceClose() {
closeSilently(delegate);
}
private void assertOpenedByCurrentThread() {
if (!openedBy.containsKey(Thread.currentThread())) {
throw new IllegalStateException("SharedFileChannel closed for current thread");
@@ -61,14 +75,22 @@ class SharedFileChannel {
readChannel = FileChannel.open(path, StandardOpenOption.READ);
}
delegate = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
if (readChannel != null) {
readChannel.close();
}
closeSilently(readChannel);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private void closeSilently(FileChannel channel) {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
// ignore
}
}
}
private void closeChannel() {
try {
delegate.close();
@@ -121,13 +143,14 @@ class SharedFileChannel {
}
}
public long transferTo(long position, long count, SharedFileChannel targetChannel) {
public long transferTo(long position, long count, SharedFileChannel targetChannel, long targetPosition) {
assertOpenedByCurrentThread();
targetChannel.assertOpenedByCurrentThread();
try {
long maxPosition = delegate.size();
long maxCount = Math.min(count, maxPosition - position);
long remaining = maxCount;
targetChannel.delegate.position(targetPosition);
while (remaining > 0) {
remaining -= delegate.transferTo(maxPosition - remaining, remaining, targetChannel.delegate);
}

View File

@@ -16,7 +16,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class NioFileSystemIntegrationTest {
public class NioFileSystemTest {
@Rule
public ExpectedException thrown = ExpectedException.none();

View File

@@ -8,6 +8,8 @@ import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.folder;
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.testFilesystem;
import static org.cryptomator.filesystem.nio.PathMatcher.doesNotExist;
import static org.cryptomator.filesystem.nio.PathMatcher.isFile;
import static org.cryptomator.filesystem.nio.ThreadStackMatcher.stackContains;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
@@ -16,19 +18,25 @@ import static org.junit.Assert.assertThat;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
@RunWith(HierarchicalContextRunner.class)
public class NioFileIntegrationTest {
public class NioFileTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@@ -339,6 +347,250 @@ public class NioFileIntegrationTest {
fileWhichIsAFolder.moveTo(target);
}
public class OpenReadable {
@Test
public void testOpenReadableReturnsAReadableNioFileForAnExistingFile() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
ReadableFile result = file.openReadable();
assertThat(result, is(instanceOf(ReadableNioFile.class)));
}
@Test
public void testOpenReadableThrowsAnUncheckedIOExceptionIfTheFileDoesNotExists() {
Path filesystemPath = emptyFilesystem();
Path nonExistingFilePath = filesystemPath.resolve("nonExistingFile");
File nonExistingFile = NioFileSystem.rootedAt(filesystemPath).file("nonExistingFile");
thrown.expect(UncheckedIOException.class);
thrown.expectMessage(nonExistingFilePath.toString());
nonExistingFile.openReadable();
}
@Test
public void testOpenReadableThrowsIllegalStateExceptionIfInvokedTwiceFromWithingTheSameThread() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openReadable();
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Current thread is already reading this file");
file.openReadable();
}
@Test
public void testOpenReadableDoesNotThrowIllegalStateExceptionIfInvokedTwiceFromWithingTheSameThreadButTheFirstReadableFileHasBeenClosed() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openReadable().close();
ReadableFile result = file.openReadable();
assertThat(result, is(instanceOf(ReadableNioFile.class)));
}
@Test
public void testOpenReadableThrowsIllegalStateExceptionIfInvokedAfterOpenWritable() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openWritable();
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Current thread is currently writing this file");
file.openReadable();
}
@Test
public void testOpenReadableDoesNotThrowIllegalStateExceptionIfInvokedAfterOpenWritableButTheWritableFileHasBeenClosed() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openWritable().close();
ReadableFile result = file.openReadable();
assertThat(result, is(instanceOf(ReadableNioFile.class)));
}
}
public class OpenWritable {
@Test
public void testOpenWritableReturnsAWritableNioFileForAnExistingFile() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
WritableFile result = file.openWritable();
assertThat(result, is(instanceOf(WritableNioFile.class)));
}
@Test
public void testOpenWritableReturnsAWritableNioFileForANonExisitingFile() {
File file = NioFileSystem.rootedAt(emptyFilesystem()).file("nonExistingFile");
WritableFile result = file.openWritable();
assertThat(result, is(instanceOf(WritableNioFile.class)));
}
@Test
public void testOpenWritableDoesNotCreateANonExisitingFile() {
Path filesystemPath = emptyFilesystem();
Path filePath = filesystemPath.resolve("nonExistingFile");
File file = NioFileSystem.rootedAt(filesystemPath).file("nonExistingFile");
file.openWritable();
assertThat(filePath, doesNotExist());
}
@Test
public void testOpenWritableThrowsIllegalStateExceptionIfInvokedTwiceFromWithingTheSameThread() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openWritable();
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Current thread is already writing this file");
file.openWritable();
}
@Test
public void testOpenWritableDoesNotThrowIllegalStateExceptionIfInvokedTwiceFromWithingTheSameThreadButTheFirstWritableFileHasBeenClosed() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openWritable().close();
WritableFile result = file.openWritable();
assertThat(result, is(instanceOf(WritableNioFile.class)));
}
@Test
public void testOpenWritableThrowsIllegalStateExceptionIfInvokedAfterOpenReadable() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openReadable();
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Current thread is currently reading this file");
file.openWritable();
}
@Test
public void testOpenWritableDoesNotThrowIllegalStateExceptionIfInvokedAfterOpenReadableButTheReadableFileHasBeenClosed() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openReadable().close();
WritableFile result = file.openWritable();
assertThat(result, is(instanceOf(WritableNioFile.class)));
}
}
public class OpenWithMultipleThreads {
@Rule
public Timeout timeout = Timeout.seconds(5);
@Test
public void testOpenReadableInvokedInTwoThreadsCompletes() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
ReadableFile readableFileFromThread1 = computeInThread(file::openReadable);
ReadableFile readableFileFromThread2 = computeInThread(file::openReadable);
assertThat(readableFileFromThread1, is(instanceOf(ReadableNioFile.class)));
assertThat(readableFileFromThread2, is(instanceOf(ReadableNioFile.class)));
assertThat(readableFileFromThread1, is(not(sameInstance(readableFileFromThread2))));
}
@Test
public void testOpenReadableInvokedWhileWritableFileIsOpenBlocks() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openWritable();
Thread thread = inThread(file::openReadable);
assertThat(thread, is(stackContains(NioFile.class, "openReadable")));
}
@Test
public void testOpenWritableInvokedWhileReadableFileIsOpenBlocks() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openReadable();
Thread thread = inThread(file::openWritable);
assertThat(thread, is(stackContains(NioFile.class, "openWritable")));
}
@Test
public void testOpenWritableInvokedWhileWritableFileIsOpenBlocks() {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
file.openWritable();
Thread thread = inThread(file::openWritable);
assertThat(thread, is(stackContains(NioFile.class, "openWritable")));
}
@Test
public void testOpenReadableInvokedWhileWritableFileIsOpenCompletesAfterClosingIt() throws InterruptedException {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
WritableFile writableFile = file.openWritable();
Thread thread = inThread(file::openReadable);
writableFile.close();
thread.join();
}
@Test
public void testOpenWritableInvokedWhileReadableFileIsOpenCompletesAfterClosingIt() throws InterruptedException {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
ReadableFile readableFile = file.openReadable();
Thread thread = inThread(file::openWritable);
readableFile.close();
thread.join();
}
@Test
public void testOpenWritableInvokedWhileWritableFileIsOpenCompletesAfterClosingIt() throws InterruptedException {
File file = NioFileSystem.rootedAt(testFilesystem(file("fileName"))).file("fileName");
WritableFile writableFile = file.openWritable();
Thread thread = inThread(file::openWritable);
writableFile.close();
thread.join();
}
private Thread inThread(Runnable task) {
Thread thread = new Thread(task);
thread.start();
try {
// give thread time to execute work
Thread.sleep(100);
} catch (InterruptedException e) {
}
return thread;
}
private <T> T computeInThread(Supplier<T> computation) {
CompletableFuture<T> future = new CompletableFuture<>();
inThread(() -> {
future.complete(computation.get());
});
try {
return future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
}
private int signum(int value) {
if (value > 0) {
return 1;

View File

@@ -31,7 +31,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class NioFolderIntegrationTest {
public class NioFolderTest {
@Rule
public ExpectedException thrown = ExpectedException.none();

View File

@@ -67,6 +67,10 @@ class PathMatcher {
return new IsFileWithContentMatcher(value);
}
public Matcher<Path> thatIsEmpty() {
return withContent(new byte[0]);
}
}
public static class IsFileWithContentMatcher extends TypeSafeDiagnosingMatcher<Path> {

View File

@@ -213,7 +213,7 @@ public class ReadableNioFileTest {
when(otherFile.belongsToSameFilesystem(file)).thenReturn(true);
long sizeOfSourceChannel = 3283;
when(channel.size()).thenReturn(sizeOfSourceChannel);
when(channel.transferTo(0, sizeOfSourceChannel, otherChannel)).thenReturn(sizeOfSourceChannel);
when(channel.transferTo(0, sizeOfSourceChannel, otherChannel, 0)).thenReturn(sizeOfSourceChannel);
inTest.copyTo(writableOtherFile);
@@ -221,7 +221,7 @@ public class ReadableNioFileTest {
inOrder.verify(writableOtherFile).assertOpen();
inOrder.verify(writableOtherFile).ensureChannelIsOpened();
inOrder.verify(otherChannel).truncate(0);
inOrder.verify(channel).transferTo(0, sizeOfSourceChannel, otherChannel);
inOrder.verify(channel).transferTo(0, sizeOfSourceChannel, otherChannel, 0);
}
@Test
@@ -235,16 +235,16 @@ public class ReadableNioFileTest {
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);
when(channel.transferTo(0, size, otherChannel, 0)).thenReturn(firstTransferAmount);
when(channel.transferTo(firstTransferAmount, sizeRemainingAfterFirstTransfer, otherChannel, firstTransferAmount)).thenReturn(secondTransferAmount);
when(channel.transferTo(firstTransferAmount + secondTransferAmount, sizeRemainingAfterSecondTransfer, otherChannel, firstTransferAmount + secondTransferAmount)).thenReturn(thirdTransferAmount);
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);
inOrder.verify(channel).transferTo(0, size, otherChannel, 0);
inOrder.verify(channel).transferTo(firstTransferAmount, sizeRemainingAfterFirstTransfer, otherChannel, firstTransferAmount);
inOrder.verify(channel).transferTo(firstTransferAmount + secondTransferAmount, sizeRemainingAfterSecondTransfer, otherChannel, firstTransferAmount + secondTransferAmount);
}
}

View File

@@ -0,0 +1,377 @@
package org.cryptomator.filesystem.nio;
import static java.util.Arrays.copyOf;
import static java.util.Arrays.copyOfRange;
import static org.apache.commons.io.FileUtils.writeByteArrayToFile;
import static org.cryptomator.filesystem.nio.PathMatcher.isFile;
import static org.cryptomator.filesystem.nio.SharedFileChannel.EOF;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.xml.ws.Holder;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
@RunWith(HierarchicalContextRunner.class)
public class SharedFileChannelTest {
private static final byte[] FILE_CONTENT = "FileContent".getBytes();
private static final byte[] OTHER_FILE_CONTENT = "Other".getBytes();
@Rule
public ExpectedException thrown = ExpectedException.none();
private Path pathOfTestfile;
private Path pathOfOtherTestfile;
private SharedFileChannel inTest;
private SharedFileChannel otherInTest;
@Before
public void setup() throws IOException {
pathOfTestfile = Files.createTempFile("sharedFileChannelTest", "tmp");
writeByteArrayToFile(pathOfTestfile.toFile(), FILE_CONTENT);
pathOfOtherTestfile = Files.createTempFile("sharedFileChannelTest", "tmp");
writeByteArrayToFile(pathOfOtherTestfile.toFile(), OTHER_FILE_CONTENT);
assertThat(pathOfTestfile, isFile().withContent(FILE_CONTENT));
assertThat(pathOfOtherTestfile, isFile().withContent(OTHER_FILE_CONTENT));
inTest = new SharedFileChannel(pathOfTestfile);
otherInTest = new SharedFileChannel(pathOfOtherTestfile);
}
public class ReadFully {
@Before
public void setup() {
inTest.open(OpenMode.READ);
}
@Test
public void testReadFullyReadsFileContents() {
byte[] result = new byte[FILE_CONTENT.length];
ByteBuffer buffer = ByteBuffer.wrap(result);
int read = inTest.readFully(0, buffer);
assertThat(result, is(FILE_CONTENT));
assertThat(read, is(FILE_CONTENT.length));
}
@Test
public void testReadFullyReadsFromPositionContents() {
int position = 5;
byte[] result = new byte[FILE_CONTENT.length - position];
ByteBuffer buffer = ByteBuffer.wrap(result);
int read = inTest.readFully(position, buffer);
assertThat(result, is(copyOfRange(FILE_CONTENT, position, FILE_CONTENT.length)));
assertThat(read, is(FILE_CONTENT.length - position));
}
@Test
public void testReadFullyReadsTillEndIfBufferHasMoreSpaceAvailabe() {
int position = 5;
byte[] result = new byte[FILE_CONTENT.length];
ByteBuffer buffer = ByteBuffer.wrap(result);
byte[] expected = copyOf(copyOfRange(FILE_CONTENT, position, FILE_CONTENT.length), FILE_CONTENT.length);
int read = inTest.readFully(position, buffer);
assertThat(result, is(expected));
assertThat(read, is(FILE_CONTENT.length - position));
}
@Test
public void testReadReadsNothingIfBufferHasNoSpaceAvailabe() {
ByteBuffer buffer = ByteBuffer.allocate(5);
buffer.position(5);
int read = inTest.readFully(0, buffer);
assertThat(buffer.array(), is(new byte[5]));
assertThat(read, is(0));
}
@Test
public void testReadReadsNothingIfPositionIsEndOfFile() {
ByteBuffer buffer = ByteBuffer.allocate(5);
int read = inTest.readFully(FILE_CONTENT.length, buffer);
assertThat(buffer.array(), is(new byte[5]));
assertThat(read, is(EOF));
}
@Test
public void testReadReadsNothingIfAfterEndOfFile() {
ByteBuffer buffer = ByteBuffer.allocate(5);
int read = inTest.readFully(FILE_CONTENT.length + 10, buffer);
assertThat(buffer.array(), is(new byte[5]));
assertThat(read, is(EOF));
}
@Test
public void testReadThrowsIllegalArgumentExceptionIfPositionIsNegative() {
thrown.expect(IllegalArgumentException.class);
inTest.readFully(-1, ByteBuffer.allocate(0));
}
}
public class Truncate {
@Before
public void setup() {
inTest.open(OpenMode.WRITE);
}
@Test
public void testTruncateToZeroDeletesFileContent() {
inTest.truncate(0);
inTest.close();
assertThat(pathOfTestfile, isFile().thatIsEmpty());
}
@Test
public void testTruncateToSmallerSizeTruncatesFile() {
inTest.truncate(5);
inTest.close();
assertThat(pathOfTestfile, isFile().withContent(copyOfRange(FILE_CONTENT, 0, 5)));
}
@Test
public void testTruncateToGreaterSizeDoesNotChangeFile() {
inTest.truncate(FILE_CONTENT.length + 10);
inTest.close();
assertThat(pathOfTestfile, isFile().withContent(FILE_CONTENT));
}
}
@Test
public void testSizeReturnsFileSize() {
inTest.open(OpenMode.READ);
assertThat(inTest.size(), is((long) FILE_CONTENT.length));
}
public class TransferTo {
@Before
public void setup() {
inTest.open(OpenMode.READ);
otherInTest.open(OpenMode.WRITE);
}
@Test
public void testTransferToCopiesSelectedRegionToTargetPosition() {
// TODO Markus Kreusch
}
@Test
public void testTransferToDoesNothingIfCountIsZero() {
// TODO Markus Kreusch
}
@Test
public void testTransferToTransfersLessThanCountBytesIfEndOfSourceFileIsReached() {
// TODO Markus Kreusch
}
@Test
public void testTransferToThrowsIllegalArgumentExceptionIfPositionIsSmallerZero() {
// TODO Markus Kreusch
}
@Test
public void testTransferToThrowsIllegalArgumentExceptionIfCountIsSmallerZero() {
// TODO Markus Kreusch
}
@Test
public void testTransferToThrowsIllegalArgumentExceptionIfTargetPositionIsSmallerZero() {
// TODO Markus Kreusch
}
@Test
public void testTransferToTransfersSelectedRegionToTargetAfterEndOfFile() {
// TODO Markus Kreusch
}
}
public class WriteFully {
@Before
public void setup() {
inTest.open(OpenMode.WRITE);
}
@Test
public void testWriteFullyWritesFileContents() {
// TODO Markus Kreusch
}
@Test
public void testWriteFullyWritesContentsToPosition() {
// TODO Markus Kreusch
}
@Test
public void testWriteFullyWritesContentsEvenIfPositionIsGreaterCurrentEndOfFile() {
// TODO Markus Kreusch
}
@Test
public void testWriteWritesNothingBufferHasNothingAvailabe() {
// TODO Markus Kreusch
}
@Test
public void testWriteThrowsIllegalArgumentExceptionIfPositionIsNegative() {
thrown.expect(IllegalArgumentException.class);
inTest.writeFully(-1, ByteBuffer.allocate(0));
}
}
public class MultipleOpenInvocation {
@Test
public void testInvokeOpenMultipleTimesFromSameThreadThrowsException() {
inTest.open(OpenMode.READ);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("A thread can only open a SharedFileChannel once");
inTest.open(OpenMode.READ);
}
@Test
public void testInvokeOpenMultipleTimesFromDifferentThreadsWorks() {
inTest.open(OpenMode.READ);
inThreadRethrowingExceptions(() -> inTest.open(OpenMode.READ));
}
}
public class NonOpenChannel {
@Test
public void testClosingNonOpenChannelThrowsException() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("SharedFileChannel closed for current thread");
inTest.close();
}
@Test
public void testInvokingReadFullyOnNonOpenChannelThrowsException() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("SharedFileChannel closed for current thread");
inTest.readFully(0, null);
}
@Test
public void testInvokingTruncateOnNonOpenChannelThrowsException() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("SharedFileChannel closed for current thread");
inTest.truncate(0);
}
@Test
public void testInvokingSizeOnNonOpenChannelThrowsException() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("SharedFileChannel closed for current thread");
inTest.size();
}
@Test
public void testInvokingTransferToOnNonOpenChannelThrowsException() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("SharedFileChannel closed for current thread");
inTest.transferTo(0, 1, null, 0);
}
@Test
public void testInvokingTransferToPassingNonOpenChannelThrowsException() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("SharedFileChannel closed for current thread");
inTest.open(OpenMode.READ);
inTest.transferTo(0, 1, otherInTest, 0);
}
@Test
public void testInvokinWriteFullyOnNonOpenChannelThrowsException() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("SharedFileChannel closed for current thread");
inTest.writeFully(0, null);
}
}
@After
@SuppressWarnings("deprecation")
public void teardown() {
inTest.forceClose();
otherInTest.forceClose();
try {
Files.delete(pathOfTestfile);
} catch (IOException e) {
// ignore
}
try {
Files.delete(pathOfOtherTestfile);
} catch (IOException e) {
// ignore
}
}
private void inThreadRethrowingExceptions(Runnable task) {
Holder<RuntimeException> holder = new Holder<>();
Thread thread = new Thread(() -> {
try {
task.run();
} catch (RuntimeException e) {
holder.value = e;
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
}
if (holder.value != null) {
throw holder.value;
}
}
}

View File

@@ -0,0 +1,58 @@
package org.cryptomator.filesystem.nio;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
class ThreadStackMatcher extends TypeSafeDiagnosingMatcher<Thread> {
public static Matcher<Thread> stackContains(Class<?> type, String methodName) {
return new ThreadStackMatcher(type, methodName);
}
private final Class<?> type;
private final String methodName;
private ThreadStackMatcher(Class<?> type, String methodName) {
super(Thread.class);
this.type = type;
this.methodName = methodName;
}
@Override
public void describeTo(Description description) {
description.appendText("a thread executing ") //
.appendValue(type) //
.appendText(".") //
.appendText(methodName);
}
@Override
protected boolean matchesSafely(Thread item, Description mismatchDescription) {
StackTraceElement[] stack = item.getStackTrace();
if (!containsNeededStackTraceElement(stack)) {
mismatchDescription.appendText("a thread not executing ") //
.appendValue(type) //
.appendText(".") //
.appendText(methodName);
return false;
} else {
return true;
}
}
private boolean containsNeededStackTraceElement(StackTraceElement[] stack) {
for (StackTraceElement stackTraceElement : stack) {
if (isNeededStackTraceElement(stackTraceElement)) {
return true;
}
}
return false;
}
private boolean isNeededStackTraceElement(StackTraceElement stackTraceElement) {
return type.getName().equals(stackTraceElement.getClassName()) //
&& methodName.equals(stackTraceElement.getMethodName());
}
}

View File

@@ -3,6 +3,8 @@ 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.Matchers.anyInt;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -158,6 +160,88 @@ public class WritableNioFileTest {
assertThat(result, is(resultOfWriteFully));
}
@Test
public void testTruncateFailsIfNotOpen() {
WritableNioFile inTest = new WritableNioFile(file);
inTest.close();
thrown.expect(UncheckedIOException.class);
thrown.expectMessage("already closed");
inTest.truncate();
}
@Test
public void testTruncateInvokesChannelsOpenWithModeWriteIfInvokedForTheFirstTimeBeforeInvokingTruncate() {
WritableNioFile inTest = new WritableNioFile(file);
inTest.truncate();
InOrder inOrder = inOrder(channel);
inOrder.verify(channel).open(WRITE);
inOrder.verify(channel).truncate(anyInt());
}
@Test
public void testTruncateChannelsTruncateWithZeroAsParameter() {
WritableNioFile inTest = new WritableNioFile(file);
inTest.truncate();
verify(channel).truncate(0);
}
@Test
public void testCloseClosesChannelIfOpenedAndUnlocksWriteLock() {
WritableNioFile inTest = new WritableNioFile(file);
inTest.truncate();
inTest.close();
InOrder inOrder = inOrder(channel, writeLock);
inOrder.verify(channel).close();
inOrder.verify(writeLock).unlock();
}
@Test
public void testCloseDoesNothingOnSecondInvocation() {
WritableNioFile inTest = new WritableNioFile(file);
inTest.truncate();
inTest.close();
inTest.close();
InOrder inOrder = inOrder(channel, writeLock);
inOrder.verify(channel).close();
inOrder.verify(writeLock).unlock();
}
@Test
public void testCloseOnlyUnlocksWriteLockIfChannelNotOpen() {
WritableNioFile inTest = new WritableNioFile(file);
inTest.close();
verify(writeLock).unlock();
verifyZeroInteractions(channel);
}
@Test
public void testCloseUnlocksWriteLockEvenIfCloseThrowsException() {
WritableNioFile inTest = new WritableNioFile(file);
inTest.truncate();
String message = "exceptionMessage";
doThrow(new RuntimeException(message)).when(channel).close();
thrown.expectMessage(message);
try {
inTest.close();
} finally {
verify(writeLock).unlock();
}
}
@Test
public void testToString() {
String fileToString = file.toString();