//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.SecureRandom;
import java.util.Random;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import gov.nasa.jpf.util.test.TestJPF;
import gov.nasa.jpf.vm.Verify;
public class SplitInputStreamTest extends TestJPF {
private static final Random s_random = Verify.isRunningInJPF() ? null : new SecureRandom();
private static final String s_jpfArgs[] = new String[]{
"+listener+=,gov.nasa.jpf.listener.PreciseRaceDetector",
"+classpath=build/classes/main"
};
private SplitInputStream m_fixture;
private InputStream m_input;
private byte m_expect[];
@Before
public void before() throws IOException {
initialize(10, SplitInputStream.INITIAL_BUFFER_SIZE);
}
public void initialize(int length, int bufferSize) throws IOException {
ByteArrayInputStream source;
int i;
m_expect = new byte[length];
if (s_random != null) {
s_random.nextBytes(m_expect);
} else {
for (i = m_expect.length; --i >= 0;) {
m_expect[i] = (byte) i;
}
}
source = new ByteArrayInputStream(m_expect);
m_fixture = new SplitInputStream(source, 2, bufferSize);
m_input = m_fixture.getStream(0);
assertEquals(2, m_fixture.getStreamCount());
}
@After
public void after() throws IOException {
InputStream input;
int i, j;
for (i = m_fixture.getStreamCount(); --i > 0;) {
input = m_fixture.getStream(i);
for (j = m_expect.length - input.available(); j < m_expect.length; j++) {
assertEquals(m_expect[j] & 0x0FF, input.read());
}
assertEquals(-1, input.read());
assertEquals(-1, input.read(new byte[1]));
}
}
@Test(expected = NullPointerException.class)
public void passNullPointerToConstructor() throws IOException {
InputStream source;
source = null;
new SplitInputStream(source, 1);
}
@Test(expected = IllegalArgumentException.class)
public void passZeroStreamsToConstructor() throws IOException {
InputStream source;
source = new ByteArrayInputStream(m_expect);
new SplitInputStream(source, 0);
}
@Test(expected = IllegalArgumentException.class)
public void passZeroBufferSizeToConstructor() throws IOException {
InputStream source;
source = new ByteArrayInputStream(m_expect);
new SplitInputStream(source, 2, 0);
}
@Test
public void readByte() throws IOException {
assertEquals(m_expect[0] & 0x0FF, m_input.read());
}
@Test
public void readEveryByteValue() throws IOException {
ByteArrayInputStream source;
int i;
m_expect = new byte[256];
for (i = 256; --i >= 0;) {
m_expect[i] = (byte) i;
}
source = new ByteArrayInputStream(m_expect);
m_fixture = new SplitInputStream(source, 2, 256);
m_input = m_fixture.getStream(0);
for (i = 0; i < 256; i++) {
assertEquals(i, m_input.read());
}
}
@Test(expected = NullPointerException.class)
public void readNullBuffer() throws IOException {
m_input.read(null, 0, 1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void readIndexNegOne() throws IOException {
m_input.read(new byte[0], -1, 0);
}
@Test(expected = IndexOutOfBoundsException.class)
public void readLengthNegOne() throws IOException {
m_input.read(new byte[0], 0, -1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void readBeyondEnd() throws IOException {
m_input.read(new byte[16], 8, 9);
}
@Test
public void readLengthZero() throws IOException {
m_input.read(new byte[1], 0, 0);
assertEquals(m_expect[0] & 0x0FF, m_input.read());
}
@Test
public void readArray() throws IOException {
int offset, length, delta;
byte buffer[];
length = m_expect.length;
buffer = new byte[length];
for (offset = 0; offset < length; offset += delta) {
delta = m_input.read(buffer);
assertTrue(delta >= 0);
}
assertArrayEquals(m_expect, buffer);
}
@Test
public void readArrayEveryByteValue() throws IOException {
ByteArrayInputStream source;
int i, delta;
byte actual[];
m_expect = new byte[256];
actual = new byte[256];
for (i = 256; --i >= 0;) {
m_expect[i] = (byte) i;
}
source = new ByteArrayInputStream(m_expect);
m_fixture = new SplitInputStream(source, 2);
m_input = m_fixture.getStream(0);
for (i = 0; i < 256; i += delta) {
delta = m_input.read(actual, i, 256 - i);
assertTrue(delta >= 0);
}
assertArrayEquals(m_expect, actual);
}
@Test
public void skipZero() throws IOException {
assertEquals(0, m_input.skip(0));
}
@Test
public void skipNegOne() throws IOException {
assertEquals(0, m_input.skip(-1));
}
@Test
public void skip() throws IOException {
assertEquals(m_expect.length / 2, m_input.skip(m_expect.length / 2));
}
@Test
public void skipToEnd() throws IOException {
assertEquals(m_expect.length, m_input.skip(m_expect.length));
}
@Test
public void skipBeyondEnd() throws IOException {
assertEquals(m_expect.length, m_input.skip(m_expect.length + 1));
assertEquals(0, m_input.skip(1));
}
@Test
public void readByteAfterClose() throws IOException {
m_input.close();
assertEquals(-1, m_input.read());
}
@Test
public void readBufferAfterClose() throws IOException {
m_input.close();
assertEquals(-1, m_input.read(new byte[1], 0, 1));
}
@Test
public void skipAfterClose() throws IOException {
m_input.close();
assertEquals(0, m_input.skip(1));
}
@Test
public void availableAfterClose() throws IOException {
assertEquals(m_expect.length, m_input.available());
m_input.close();
assertEquals(0, m_input.available());
}
@Test
public void availableAtEnd() throws IOException {
int i;
for (i = 0; i < m_expect.length; i++) {
assertEquals(m_expect.length - i, m_input.available());
assertEquals(m_expect[i] & 0x0FF, m_input.read());
}
assertEquals(0, m_input.available());
}
@Test
public void availableNeverReads() throws IOException {
SplitInputStream split;
InputStream source, stream;
source = new InputStream() {
public int read() {
fail();
return (0);
}
};
split = new SplitInputStream(source, 1);
stream = split.getStream(0);
assertEquals(0, stream.available());
}
@Test
public void closeSource() throws IOException {
SplitInputStream split;
CloseCountInputStream source;
InputStream input;
int i;
source = new CloseCountInputStream();
split = new SplitInputStream(source, 2);
input = split.getStream(0);
for (i = split.getStreamCount() + 5; --i >= 0;) {
input.close();
}
assertEquals(0, source.getCloseCount());
input = split.getStream(1);
input.close();
assertEquals(1, source.getCloseCount());
for (i = split.getStreamCount() + 5; --i >= 0;) {
input.close();
}
assertEquals(1, source.getCloseCount());
}
@Test
public void overflowAvailable() throws IOException {
SplitInputStream split;
MaxAvailableInputStream source;
InputStream input;
source = new MaxAvailableInputStream();
split = new SplitInputStream(source, 1);
input = split.getStream(0);
assertEquals(0, input.read());
assertEquals(Integer.MAX_VALUE, input.available());
}
@Test
public void expand() throws IOException {
int i, length;
length = 2 * SplitInputStream.INITIAL_BUFFER_SIZE;
initialize(length, SplitInputStream.INITIAL_BUFFER_SIZE);
for (i = 0; i < length; i++) {
assertEquals(length - i, m_input.available());
assertEquals(m_expect[i] & 0x0FF, m_input.read());
}
}
@Test
public void wrap() throws IOException {
int i, length;
length = 2 * SplitInputStream.INITIAL_BUFFER_SIZE;
initialize(length, SplitInputStream.INITIAL_BUFFER_SIZE);
for (i = 0; i < SplitInputStream.INITIAL_BUFFER_SIZE - 5; i++) {
assertEquals(m_expect[i] & 0x0FF, m_input.read());
}
m_input = m_fixture.getStream(1);
for (i = 0; i < SplitInputStream.INITIAL_BUFFER_SIZE; i++) {
assertEquals(m_expect[i] & 0x0FF, m_input.read());
}
assertEquals(SplitInputStream.INITIAL_BUFFER_SIZE, m_input.available());
}
@Test
public void ignoreClosedStream() throws IOException, NoSuchFieldException, IllegalAccessException {
Field bufferField;
int i, length;
byte expect[], actual[];
length = 2 * SplitInputStream.INITIAL_BUFFER_SIZE;
initialize(length, SplitInputStream.INITIAL_BUFFER_SIZE);
m_input.close();
bufferField = SplitInputStream.class.getDeclaredField("m_buffer");
bufferField.setAccessible(true);
expect = (byte[]) bufferField.get(m_fixture);
m_input = m_fixture.getStream(1);
for (i = 0; i < length; i++) {
assertEquals(m_expect[i] & 0x0FF, m_input.read());
}
actual = (byte[]) bufferField.get(m_fixture);
assertSame(expect, actual);
}
@Test
public void bufferSize1() throws IOException {
int i;
initialize(10, 1);
for (i = 0; i < m_expect.length; i++) {
assertEquals(m_expect[i] & 0x0FF, m_input.read());
}
}
@Ignore("This test takes too long for everyone to run all the time. There are 101,174 states which take about 4 minutes to run.")
@Test
public void concurrentRead() throws IOException, InterruptedException {
InputStream source;
Thread thread1, thread2;
Runnable task;
if (verifyNoPropertyViolation(s_jpfArgs)) {
source = new InputStream() {
private Thread m_reader;
public int read() {
if (m_reader == null) {
m_reader = Thread.currentThread(); // JPF will catch the race condition if 2 threads call read concurrently.
} else {
assertSame(m_reader, Thread.currentThread());
}
return (0);
}
};
m_fixture = new SplitInputStream(source, 2);
task = new Runnable() {
public void run() {
try {
m_fixture.getStream(0).read();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
thread1 = new Thread(task);
task = new Runnable() {
public void run() {
try {
m_fixture.getStream(1).read();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
@Test
public void concurrentAvailable() throws InterruptedException {
InputStream source;
Thread thread1, thread2;
Runnable task;
if (verifyNoPropertyViolation(s_jpfArgs)) {
source = new InputStream() {
private Thread m_access;
public int read() {
fail();
return (0);
}
public int available() {
assertNull(m_access);
m_access = Thread.currentThread(); // JPF will catch the race condition if 2 threads call concurrently.
m_access = null;
return (0);
}
};
m_fixture = new SplitInputStream(source, 2);
task = new Runnable() {
public void run() {
try {
m_fixture.getStream(0).available();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
thread1 = new Thread(task);
task = new Runnable() {
public void run() {
try {
m_fixture.getStream(1).available();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
@Ignore("This test takes too long for everyone to run all the time. There are 230,360 states which take about 5 minutes to run.")
@Test
public void thoroughJPFTest() throws InterruptedException, IOException {
Thread thread1, thread2;
if (verifyNoPropertyViolation(s_jpfArgs)) {
initialize(4, 2);
thread1 = new Thread(new JPFTask(0));
thread2 = new Thread(new JPFTask(1));
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
private static class CloseCountInputStream extends InputStream {
private int m_closeCount;
public int read() {
return (0);
}
public int available() {
return (m_closeCount == 0 ? 1 : 0);
}
public void close() {
m_closeCount++;
}
public int getCloseCount() {
return (m_closeCount);
}
}
private static class MaxAvailableInputStream extends InputStream {
private int m_data;
public int read() {
return (m_data++);
}
public int available() {
return (Integer.MAX_VALUE);
}
}
private class JPFTask implements Runnable {
private final InputStream m_input;
public JPFTask(int index) {
m_input = m_fixture.getStream(index);
}
public void run() {
try {
unsafe();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void unsafe() throws IOException {
int i, expect, actual, test;
byte buffer[];
//System.out.print("#" + Thread.currentThread().getId() + " | Test ");
test = Verify.getInt(0, 4);
//System.out.println(test);
switch (test) {
case 0:
m_input.close();
break;
case 1:
assertEquals(4, m_input.available());
break;
case 2:
expect = Verify.getInt(-1, 5);
actual = (int) m_input.skip(expect);
expect = Math.max(expect, 0);
expect = Math.min(expect, 4);
assertTrue(actual <= expect);
break;
case 3:
for (i = 0; i < 4; i++) {
assertEquals(m_expect[i], m_input.read());
}
break;
case 4:
buffer = new byte[1];
for (i = 0; i < 4; i++) {
assertEquals(1, m_input.read(buffer));
assertEquals(m_expect[i], buffer[0]);
}
break;
}
}
}
}