/*
* Copyright 2013-2016 EMC Corporation. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.emc.ecs.sync.util;
import com.emc.object.util.ProgressListener;
import org.junit.Assert;
import org.junit.Test;
import java.io.*;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
public class ParallelInputStreamTest {
@Test
public void testStandardRead() throws Exception {
byte[] content = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".getBytes("UTF-8");
ParallelInputStream pStream = new ParallelInputStream(new ByteArrayInputStream(content), 1024);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
streamAndClose(pStream, baos);
Assert.assertTrue(pStream.reader.isComplete());
Assert.assertFalse(pStream.reader.isFailed());
Assert.assertFalse(pStream.readerThread.isAlive());
}
@Test
public void testReadFailure() throws Exception {
byte[] content = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!abcdefghijklmnopqrstuvwxyz0123456789".getBytes("UTF-8");
String message = "bang";
// use 2-byte buffer to ensure the reader thread doesn't fail too early
InputStream rawStream = new FailingInputStream(new ByteArrayInputStream(content), '!', message);
ParallelInputStream pStream = new ParallelInputStream(rawStream, 2);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
streamAndClose(pStream, baos);
Assert.fail("stream should have thrown exception");
} catch (IOException e) {
Assert.assertEquals("bang", e.getCause().getMessage());
}
Assert.assertEquals(24, baos.size()); // should fail {bufferSize} characters before the bang
Assert.assertFalse(pStream.reader.isComplete());
Assert.assertTrue(pStream.reader.isFailed());
Assert.assertFalse(pStream.readerThread.isAlive());
}
@Test
public void testWriteFailure() throws Exception {
byte[] content = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".getBytes("UTF-8");
final AtomicLong readCount = new AtomicLong();
ProgressListener readListener = new ProgressListener() {
@Override
public void progress(long completed, long total) {
}
@Override
public void transferred(long size) {
readCount.addAndGet(size);
}
};
// 2-byte buffer ensures that the reader thread doesn't read too far ahead
ParallelInputStream pStream = new ParallelInputStream(new ByteArrayInputStream(content), 2, readListener);
// read 10 bytes
byte[] buffer = new byte[10];
int read, total = 0;
do {
read = pStream.read(buffer, total, buffer.length - total);
System.out.println("read " + read + " bytes");
if (read > 0) total += read;
} while (read != -1 && total < 10);
// close stream early as though a write error occurred in target storage
pStream.close();
Assert.assertEquals(10, total);
Assert.assertArrayEquals(Arrays.copyOfRange(content, 0, 10), buffer);
Assert.assertFalse(pStream.reader.isComplete());
Assert.assertTrue(pStream.reader.isFailed());
Assert.assertFalse(pStream.readerThread.isAlive());
}
private void streamAndClose(InputStream in, OutputStream out) throws IOException {
try (InputStream inStream = in;
OutputStream outStream = out) {
byte[] buffer = new byte[1024];
int read;
while (((read = inStream.read(buffer)) != -1)) {
outStream.write(buffer, 0, read);
}
}
}
private class FailingInputStream extends FilterInputStream {
private char failChar;
private String errorMessage;
private IOException error;
public FailingInputStream(InputStream in, char failChar, String errorMessage) {
super(in);
this.failChar = failChar;
this.errorMessage = errorMessage;
}
@Override
public int read() throws IOException {
int result = super.read();
if ((char) result == failChar) throw new IOException(errorMessage);
return result;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
// read all bytes prior to fail character, then throw exception on next read; this is for testing purposes
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (error != null) throw error;
int i = 0;
try {
for (; i < len; i++) {
int c = read();
if (c == -1 || error != null) break;
b[off + i] = (byte) c;
}
} catch (IOException e) {
error = e;
}
return i == 0 ? -1 : i;
}
}
}