package rocks.inspectit.shared.cs.storage.nio;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.mockito.Mockito;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import rocks.inspectit.shared.all.storage.nio.stream.ExtendedByteBufferOutputStream;
import rocks.inspectit.shared.cs.storage.nio.read.ReadingChannelManager;
import rocks.inspectit.shared.cs.storage.nio.write.WritingChannelManager;
/**
* Test's if the data wrote by a {@link WritingChannelManager} is readable by
* {@link ReadingChannelManager}.
*
* @author Ivan Senic
*
*/
@SuppressWarnings("PMD")
public class ChannelManagersTest {
/**
* File where data will be written/read.
*/
private final Path file = Paths.get("src/test/java/testFile.");
/**
* Write manager.
*/
private WritingChannelManager writingChannelManager;
/**
* Read manager.
*/
private ReadingChannelManager readingChannelManager;
/**
* Initializes the channel managers that will use default executor service.
*/
@BeforeClass
public void initChannelManagers() {
writingChannelManager = new WritingChannelManager();
readingChannelManager = new ReadingChannelManager();
}
/**
* Tests if writing and then reading the set of fixed sizes bytes will be correct.
*
* @throws IOException
* With {@link IOException}.
* @throws InterruptedException
* With {@link InterruptedException}.
*/
@Test(invocationCount = 10)
public void writeReadFixedSize() throws IOException, InterruptedException {
final LinkedBlockingQueue<ByteBuffer> bufferQueue = new LinkedBlockingQueue<>();
byte[] bytes = getRandomByteArray();
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bytes.length);
byteBuffer.put(bytes);
byteBuffer.flip();
long position = writingChannelManager.write(byteBuffer, file, new WriteReadCompletionRunnable() {
@Override
public void run() {
byteBuffer.clear();
bufferQueue.add(byteBuffer);
}
});
final ByteBuffer readBuffer = bufferQueue.take();
readingChannelManager.read(readBuffer, position, bytes.length, file, new WriteReadCompletionRunnable() {
@Override
public void run() {
bufferQueue.add(readBuffer);
}
});
byte[] readBytes = new byte[bytes.length];
bufferQueue.take().get(readBytes);
assertThat(readBytes, is(equalTo(bytes)));
readingChannelManager.finalizeChannel(file);
writingChannelManager.finalizeChannel(file);
}
/**
* Tests if writing and then reading the set of unknown bytes size will be correct.
*
* @throws IOException
* With {@link IOException}.
* @throws InterruptedException
* With {@link InterruptedException}.
*/
@Test(invocationCount = 10)
public void writeReadUnknownSize() throws IOException, InterruptedException {
final LinkedBlockingQueue<ByteBuffer> bufferQueue = new LinkedBlockingQueue<>();
byte[] bytes = getRandomByteArray();
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bytes.length);
byteBuffer.put(bytes);
byteBuffer.flip();
long position = writingChannelManager.write(byteBuffer, file, new WriteReadCompletionRunnable() {
@Override
public void run() {
byteBuffer.clear();
bufferQueue.add(byteBuffer);
}
});
final ByteBuffer readBuffer = bufferQueue.take();
readingChannelManager.read(readBuffer, position, 0, file, new WriteReadCompletionRunnable() {
@Override
public void run() {
bufferQueue.add(readBuffer);
}
});
byte[] readBytes = new byte[bytes.length];
bufferQueue.take().get(readBytes);
assertThat(readBytes, is(equalTo(bytes)));
writingChannelManager.finalizeChannel(file);
readingChannelManager.finalizeChannel(file);
}
/**
* Test the write in the
* {@link WritingChannelManager#write(ExtendedByteBufferOutputStream, Path, WriteReadCompletionRunnable)}
* . {@link ExtendedByteBufferOutputStream} is mocked.
*
* @throws IOException
* If {@link IOException} occurs.
* @throws InterruptedException
* If thread is interruped.
*/
@Test(invocationCount = 10)
public void writeWithExtendedByteBufferOutputStream() throws IOException, InterruptedException {
final byte[] bytes = getRandomByteArray();
int bufferSize = 1024 * 1024;
List<ByteBuffer> buffers = new ArrayList<>();
for (int position = 0; position < (bytes.length - 1);) {
int size = Math.min(bufferSize, bytes.length - position);
ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
buffer.put(bytes, position, size);
buffer.flip();
buffers.add(buffer);
position += size;
}
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
ExtendedByteBufferOutputStream outputStream = Mockito.mock(ExtendedByteBufferOutputStream.class);
Mockito.when(outputStream.getAllByteBuffers()).thenReturn(buffers);
Mockito.when(outputStream.getTotalWriteSize()).thenReturn((long) bytes.length);
long position = writingChannelManager.write(outputStream, file, new WriteReadCompletionRunnable(buffers.size()) {
@Override
public void run() {
assertThat(isCompleted(), is(true));
assertThat(getAttemptedWriteReadSize(), is(equalTo((long) bytes.length)));
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
});
lock.lock();
try {
condition.await(30, TimeUnit.SECONDS);
} finally {
lock.unlock();
}
InputStream is = Files.newInputStream(file, StandardOpenOption.READ);
is.skip(position);
byte[] actual = new byte[bytes.length];
is.read(actual);
assertThat(actual, is(equalTo(bytes)));
}
/**
* Deletes the created file.
*
* @throws IOException
* If {@link IOException} occurs.
*/
@AfterTest
public void deleteFile() throws IOException {
if (Files.exists(file)) {
// make sure file is delete-able
assertThat(Files.deleteIfExists(file), is(true));
}
}
/**
* Random size byte array.
*
* @return Random size byte array.
*/
private static byte[] getRandomByteArray() {
Random random = new Random();
// max 10MB
int length = random.nextInt(10 * 1024 * 1024);
byte[] array = new byte[length];
random.nextBytes(array);
return array;
}
}