mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 18:51:26 +00:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user