/* * Copyright (C) 2007 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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.google.common.io; import static com.google.common.io.ByteStreams.copy; import static com.google.common.io.ByteStreams.newInputStreamSupplier; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Bytes; import com.google.common.testing.TestLogHandler; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.FilterInputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.Arrays; import java.util.Random; /** * Unit test for {@link ByteStreams}. * * @author Chris Nokleberg */ public class ByteStreamsTest extends IoTestCase { /** Provides an InputStream that throws an IOException on every read. */ static final InputSupplier<InputStream> BROKEN_READ = new InputSupplier<InputStream>() { @Override public InputStream getInput() { return new InputStream() { @Override public int read() throws IOException { throw new IOException("broken read"); } }; } }; /** Provides an OutputStream that throws an IOException on every write. */ static final OutputSupplier<OutputStream> BROKEN_WRITE = new OutputSupplier<OutputStream>() { @Override public OutputStream getOutput() { return new OutputStream() { @Override public void write(int b) throws IOException { throw new IOException("broken write"); } }; } }; /** Provides an InputStream that throws an IOException on close. */ static final InputSupplier<InputStream> BROKEN_CLOSE_INPUT = new InputSupplier<InputStream>() { @Override public InputStream getInput() { return new FilterInputStream(new ByteArrayInputStream(new byte[10])) { @Override public void close() throws IOException { throw new IOException("broken close input"); } }; } }; /** Provides an OutputStream that throws an IOException on every close. */ static final OutputSupplier<OutputStream> BROKEN_CLOSE_OUTPUT = new OutputSupplier<OutputStream>() { @Override public OutputStream getOutput() { return new FilterOutputStream(new ByteArrayOutputStream()) { @Override public void close() throws IOException { throw new IOException("broken close output"); } }; } }; /** Throws an IOException from getInput. */ static final InputSupplier<InputStream> BROKEN_GET_INPUT = new InputSupplier<InputStream>() { @Override public InputStream getInput() throws IOException { throw new IOException("broken get input"); } }; /** Throws an IOException from getOutput. */ static final OutputSupplier<OutputStream> BROKEN_GET_OUTPUT = new OutputSupplier<OutputStream>() { @Override public OutputStream getOutput() throws IOException { throw new IOException("broken get output"); } }; private static final ImmutableSet<InputSupplier<InputStream>> BROKEN_INPUTS = ImmutableSet.of(BROKEN_CLOSE_INPUT, BROKEN_GET_INPUT, BROKEN_READ); private static final ImmutableSet<OutputSupplier<OutputStream>> BROKEN_OUTPUTS = ImmutableSet.of(BROKEN_CLOSE_OUTPUT, BROKEN_GET_OUTPUT, BROKEN_WRITE); public void testByteSuppliers() throws IOException { byte[] range = newPreFilledByteArray(200); assertTrue(Arrays.equals(range, ByteStreams.toByteArray(ByteStreams.newInputStreamSupplier(range)))); byte[] subRange = ByteStreams.toByteArray( ByteStreams.newInputStreamSupplier(range, 100, 50)); assertEquals(50, subRange.length); assertEquals(100, subRange[0]); assertEquals((byte) 149, subRange[subRange.length - 1]); } public void testEqual() throws IOException { equalHelper(false, 0, 1); equalHelper(false, 400, 10000); equalHelper(false, 0x2000, 0x2001); equalHelper(false, new byte[]{ 0 }, new byte[]{ 1 }); byte[] mutate = newPreFilledByteArray(10000); mutate[9000] = 0; equalHelper(false, mutate, newPreFilledByteArray(10000)); equalHelper(true, 0, 0); equalHelper(true, 1, 1); equalHelper(true, 400, 400); final byte[] tenK = newPreFilledByteArray(10000); equalHelper(true, tenK, tenK); assertTrue(ByteStreams.equal(ByteStreams.newInputStreamSupplier(tenK), new InputSupplier<InputStream>() { @Override public InputStream getInput() { return new RandomAmountInputStream(new ByteArrayInputStream(tenK), new Random(301)); } })); } private void equalHelper(boolean expect, int size1, int size2) throws IOException { equalHelper(expect, newPreFilledByteArray(size1), newPreFilledByteArray(size2)); } private void equalHelper(boolean expect, byte[] a, byte[] b) throws IOException { assertEquals(expect, ByteStreams.equal( ByteStreams.newInputStreamSupplier(a), ByteStreams.newInputStreamSupplier(b))); } public void testAlwaysCloses() throws IOException { byte[] range = newPreFilledByteArray(100); CheckCloseSupplier.Input<InputStream> okRead = newCheckInput(ByteStreams.newInputStreamSupplier(range)); CheckCloseSupplier.Output<OutputStream> okWrite = newCheckOutput(new OutputSupplier<OutputStream>() { @Override public OutputStream getOutput() { return new ByteArrayOutputStream(); } }); CheckCloseSupplier.Input<InputStream> brokenRead = newCheckInput(BROKEN_READ); CheckCloseSupplier.Output<OutputStream> brokenWrite = newCheckOutput(BROKEN_WRITE); // copy, both suppliers ByteStreams.copy(okRead, okWrite); assertTrue(okRead.areClosed()); assertTrue(okWrite.areClosed()); try { ByteStreams.copy(okRead, brokenWrite); fail("expected exception"); } catch (IOException e) { assertEquals("broken write", e.getMessage()); } assertTrue(okRead.areClosed()); assertTrue(brokenWrite.areClosed()); try { ByteStreams.copy(brokenRead, okWrite); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertTrue(brokenRead.areClosed()); assertTrue(okWrite.areClosed()); try { ByteStreams.copy(brokenRead, brokenWrite); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertTrue(brokenRead.areClosed()); assertTrue(brokenWrite.areClosed()); // copy, input supplier OutputStream out = okWrite.getOutput(); ByteStreams.copy(okRead, out); assertTrue(okRead.areClosed()); assertFalse(okWrite.areClosed()); out.close(); out = brokenWrite.getOutput(); try { ByteStreams.copy(okRead, out); fail("expected exception"); } catch (IOException e) { assertEquals("broken write", e.getMessage()); } assertTrue(okRead.areClosed()); assertFalse(brokenWrite.areClosed()); out.close(); out = okWrite.getOutput(); try { ByteStreams.copy(brokenRead, out); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertTrue(brokenRead.areClosed()); assertFalse(okWrite.areClosed()); out.close(); out = brokenWrite.getOutput(); try { ByteStreams.copy(brokenRead, out); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertTrue(brokenRead.areClosed()); assertFalse(brokenWrite.areClosed()); out.close(); // copy, output supplier InputStream in = okRead.getInput(); ByteStreams.copy(in, okWrite); assertFalse(okRead.areClosed()); assertTrue(okWrite.areClosed()); in.close(); in = okRead.getInput(); try { ByteStreams.copy(in, brokenWrite); fail("expected exception"); } catch (IOException e) { assertEquals("broken write", e.getMessage()); } assertFalse(okRead.areClosed()); assertTrue(brokenWrite.areClosed()); in.close(); in = brokenRead.getInput(); try { ByteStreams.copy(in, okWrite); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertFalse(brokenRead.areClosed()); assertTrue(okWrite.areClosed()); in.close(); in = brokenRead.getInput(); try { ByteStreams.copy(in, brokenWrite); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertFalse(brokenRead.areClosed()); assertTrue(brokenWrite.areClosed()); in.close(); // toByteArray assertTrue(Arrays.equals(range, ByteStreams.toByteArray(okRead))); assertTrue(okRead.areClosed()); try { ByteStreams.toByteArray(brokenRead); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertTrue(brokenRead.areClosed()); // equal try { ByteStreams.equal(brokenRead, okRead); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertTrue(brokenRead.areClosed()); try { ByteStreams.equal(okRead, brokenRead); fail("expected exception"); } catch (IOException e) { assertEquals("broken read", e.getMessage()); } assertTrue(brokenRead.areClosed()); // write try { ByteStreams.write(new byte[10], brokenWrite); fail("expected exception"); } catch (IOException e) { assertEquals("broken write", e.getMessage()); } assertTrue(brokenWrite.areClosed()); } private static int getAndResetRecords(TestLogHandler logHandler) { int records = logHandler.getStoredLogRecords().size(); logHandler.clear(); return records; } private static void runFailureTest( InputSupplier<? extends InputStream> in, OutputSupplier<OutputStream> out) { try { copy(in, out); fail(); } catch (IOException expected) { } } private static OutputSupplier<OutputStream> newByteArrayOutputStreamSupplier() { return new OutputSupplier<OutputStream>() { @Override public OutputStream getOutput() { return new ByteArrayOutputStream(); } }; } public void testWriteBytes() throws IOException { final ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] expected = newPreFilledByteArray(100); ByteStreams.write(expected, new OutputSupplier<OutputStream>() { @Override public OutputStream getOutput() { return out; } }); assertTrue(Arrays.equals(expected, out.toByteArray())); } public void testCopy() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] expected = newPreFilledByteArray(100); long num = ByteStreams.copy(new ByteArrayInputStream(expected), out); assertEquals(100, num); assertTrue(Arrays.equals(expected, out.toByteArray())); } public void testCopyChannel() throws IOException { byte[] expected = newPreFilledByteArray(100); ByteArrayOutputStream out = new ByteArrayOutputStream(); WritableByteChannel outChannel = Channels.newChannel(out); ReadableByteChannel inChannel = Channels.newChannel(new ByteArrayInputStream(expected)); ByteStreams.copy(inChannel, outChannel); assertTrue(Arrays.equals(expected, out.toByteArray())); } public void testReadFully() throws IOException { byte[] b = new byte[10]; try { ByteStreams.readFully(newTestStream(10), null, 0, 10); fail("expected exception"); } catch (NullPointerException e) { } try { ByteStreams.readFully(null, b, 0, 10); fail("expected exception"); } catch (NullPointerException e) { } try { ByteStreams.readFully(newTestStream(10), b, -1, 10); fail("expected exception"); } catch (IndexOutOfBoundsException e) { } try { ByteStreams.readFully(newTestStream(10), b, 0, -1); fail("expected exception"); } catch (IndexOutOfBoundsException e) { } try { ByteStreams.readFully(newTestStream(10), b, 0, -1); fail("expected exception"); } catch (IndexOutOfBoundsException e) { } try { ByteStreams.readFully(newTestStream(10), b, 2, 10); fail("expected exception"); } catch (IndexOutOfBoundsException e) { } try { ByteStreams.readFully(newTestStream(5), b, 0, 10); fail("expected exception"); } catch (EOFException e) { } Arrays.fill(b, (byte) 0); ByteStreams.readFully(newTestStream(10), b, 0, 0); assertTrue(Arrays.equals(new byte[10], b)); Arrays.fill(b, (byte) 0); ByteStreams.readFully(newTestStream(10), b, 0, 10); assertTrue(Arrays.equals(newPreFilledByteArray(10), b)); Arrays.fill(b, (byte) 0); ByteStreams.readFully(newTestStream(10), b, 0, 5); assertTrue(Arrays.equals(new byte[]{0, 1, 2, 3, 4, 0, 0, 0, 0, 0}, b)); } public void testSkipFully() throws IOException { byte[] bytes = newPreFilledByteArray(100); skipHelper(0, 0, new ByteArrayInputStream(bytes)); skipHelper(50, 50, new ByteArrayInputStream(bytes)); skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 1)); skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 0)); skipHelper(100, -1, new ByteArrayInputStream(bytes)); try { skipHelper(101, 0, new ByteArrayInputStream(bytes)); fail("expected exception"); } catch (EOFException e) { } } private void skipHelper(long n, int expect, InputStream in) throws IOException { ByteStreams.skipFully(in, n); assertEquals(expect, in.read()); in.close(); } // TODO(user): rename; violates rule that only immutable things can be all caps private static final byte[] BYTES = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10 }; public void testNewDataInput_empty() { byte[] b = new byte[0]; ByteArrayDataInput in = ByteStreams.newDataInput(b); try { in.readInt(); fail(); } catch (IllegalStateException expected) { } } public void testNewDataInput_normal() { ByteArrayDataInput in = ByteStreams.newDataInput(BYTES); assertEquals(0x12345678, in.readInt()); assertEquals(0x76543210, in.readInt()); try { in.readInt(); fail(); } catch (IllegalStateException expected) { } } public void testNewDataInput_readFully() { ByteArrayDataInput in = ByteStreams.newDataInput(BYTES); byte[] actual = new byte[BYTES.length]; in.readFully(actual); assertEquals(BYTES, actual); } public void testNewDataInput_readFullyAndThenSome() { ByteArrayDataInput in = ByteStreams.newDataInput(BYTES); byte[] actual = new byte[BYTES.length * 2]; try { in.readFully(actual); fail(); } catch (IllegalStateException ex) { assertTrue(ex.getCause() instanceof EOFException); } } public void testNewDataInput_readFullyWithOffset() { ByteArrayDataInput in = ByteStreams.newDataInput(BYTES); byte[] actual = new byte[4]; in.readFully(actual, 2, 2); assertEquals(0, actual[0]); assertEquals(0, actual[1]); assertEquals(BYTES[0], actual[2]); assertEquals(BYTES[1], actual[3]); } public void testNewDataInput_readLine() { ByteArrayDataInput in = ByteStreams.newDataInput( "This is a line\r\nThis too\rand this\nand also this".getBytes(Charsets.UTF_8)); assertEquals("This is a line", in.readLine()); assertEquals("This too", in.readLine()); assertEquals("and this", in.readLine()); assertEquals("and also this", in.readLine()); } public void testNewDataInput_readFloat() { byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10}; ByteArrayDataInput in = ByteStreams.newDataInput(data); assertEquals(Float.intBitsToFloat(0x12345678), in.readFloat(), 0.0); assertEquals(Float.intBitsToFloat(0x76543210), in.readFloat(), 0.0); } public void testNewDataInput_readDouble() { byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10}; ByteArrayDataInput in = ByteStreams.newDataInput(data); assertEquals(Double.longBitsToDouble(0x1234567876543210L), in.readDouble(), 0.0); } public void testNewDataInput_readUTF() { byte[] data = new byte[17]; data[1] = 15; System.arraycopy("Kilroy was here".getBytes(Charsets.UTF_8), 0, data, 2, 15); ByteArrayDataInput in = ByteStreams.newDataInput(data); assertEquals("Kilroy was here", in.readUTF()); } public void testNewDataInput_readChar() { byte[] data = "qed".getBytes(Charsets.UTF_16BE); ByteArrayDataInput in = ByteStreams.newDataInput(data); assertEquals('q', in.readChar()); assertEquals('e', in.readChar()); assertEquals('d', in.readChar()); } public void testNewDataInput_readUnsignedShort() { byte[] data = {0, 0, 0, 1, (byte) 0xFF, (byte) 0xFF, 0x12, 0x34}; ByteArrayDataInput in = ByteStreams.newDataInput(data); assertEquals(0, in.readUnsignedShort()); assertEquals(1, in.readUnsignedShort()); assertEquals(65535, in.readUnsignedShort()); assertEquals(0x1234, in.readUnsignedShort()); } public void testNewDataInput_readLong() { byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10}; ByteArrayDataInput in = ByteStreams.newDataInput(data); assertEquals(0x1234567876543210L, in.readLong()); } public void testNewDataInput_readBoolean() { ByteArrayDataInput in = ByteStreams.newDataInput(BYTES); assertTrue(in.readBoolean()); } public void testNewDataInput_readByte() { ByteArrayDataInput in = ByteStreams.newDataInput(BYTES); for (int i = 0; i < BYTES.length; i++) { assertEquals(BYTES[i], in.readByte()); } try { in.readByte(); fail(); } catch (IllegalStateException ex) { assertTrue(ex.getCause() instanceof EOFException); } } public void testNewDataInput_readUnsignedByte() { ByteArrayDataInput in = ByteStreams.newDataInput(BYTES); for (int i = 0; i < BYTES.length; i++) { assertEquals(BYTES[i], in.readUnsignedByte()); } try { in.readUnsignedByte(); fail(); } catch (IllegalStateException ex) { assertTrue(ex.getCause() instanceof EOFException); } } public void testNewDataInput_offset() { ByteArrayDataInput in = ByteStreams.newDataInput(BYTES, 2); assertEquals(0x56787654, in.readInt()); try { in.readInt(); fail(); } catch (IllegalStateException expected) { } } public void testNewDataInput_skip() { ByteArrayDataInput in = ByteStreams.newDataInput(new byte[2]); in.skipBytes(2); try { in.skipBytes(1); } catch (IllegalStateException expected) { } } public void testNewDataOutput_empty() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); assertEquals(0, out.toByteArray().length); } public void testNewDataOutput_writeInt() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeInt(0x12345678); out.writeInt(0x76543210); assertTrue(Arrays.equals(BYTES, out.toByteArray())); } public void testNewDataOutput_sized() { ByteArrayDataOutput out = ByteStreams.newDataOutput(4); out.writeInt(0x12345678); out.writeInt(0x76543210); assertTrue(Arrays.equals(BYTES, out.toByteArray())); } public void testNewDataOutput_writeLong() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeLong(0x1234567876543210L); assertTrue(Arrays.equals(BYTES, out.toByteArray())); } public void testNewDataOutput_writeByteArray() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.write(BYTES); assertTrue(Arrays.equals(BYTES, out.toByteArray())); } public void testNewDataOutput_writeByte() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.write(0x12); out.writeByte(0x34); assertTrue(Arrays.equals(new byte[] {0x12, 0x34}, out.toByteArray())); } public void testNewDataOutput_writeByteOffset() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.write(BYTES, 4, 2); byte[] expected = {BYTES[4], BYTES[5]}; assertEquals(expected, out.toByteArray()); } public void testNewDataOutput_writeBoolean() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeBoolean(true); out.writeBoolean(false); byte[] expected = {(byte) 1, (byte) 0}; assertEquals(expected, out.toByteArray()); } public void testNewDataOutput_writeChar() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeChar('a'); assertTrue(Arrays.equals(new byte[] {0, 97}, out.toByteArray())); } public void testNewDataOutput_writeChars() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeChars("r\u00C9sum\u00C9"); // need to remove byte order mark before comparing byte[] expected = Arrays.copyOfRange("r\u00C9sum\u00C9".getBytes(Charsets.UTF_16), 2, 14); assertEquals(expected, out.toByteArray()); } public void testNewDataOutput_writeUTF() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeUTF("r\u00C9sum\u00C9"); byte[] expected ="r\u00C9sum\u00C9".getBytes(Charsets.UTF_8); byte[] actual = out.toByteArray(); // writeUTF writes the length of the string in 2 bytes assertEquals(0, actual[0]); assertEquals(expected.length, actual[1]); assertEquals(expected, Arrays.copyOfRange(actual, 2, actual.length)); } public void testNewDataOutput_writeShort() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeShort(0x1234); assertTrue(Arrays.equals(new byte[] {0x12, 0x34}, out.toByteArray())); } public void testNewDataOutput_writeDouble() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeDouble(Double.longBitsToDouble(0x1234567876543210L)); assertEquals(BYTES, out.toByteArray()); } public void testNewDataOutput_writeFloat() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeFloat(Float.intBitsToFloat(0x12345678)); out.writeFloat(Float.intBitsToFloat(0x76543210)); assertEquals(BYTES, out.toByteArray()); } public void testLength() throws IOException { lengthHelper(Long.MAX_VALUE); lengthHelper(7); lengthHelper(1); lengthHelper(0); assertEquals(0, ByteStreams.length( ByteStreams.newInputStreamSupplier(new byte[0]))); } private void lengthHelper(final long skipLimit) throws IOException { assertEquals(100, ByteStreams.length(new InputSupplier<InputStream>() { @Override public InputStream getInput() { return new SlowSkipper(new ByteArrayInputStream(new byte[100]), skipLimit); } })); } public void testSlice() throws IOException { // Test preconditions InputSupplier<? extends InputStream> supplier = ByteStreams.newInputStreamSupplier(newPreFilledByteArray(100)); try { ByteStreams.slice(supplier, -1, 10); fail("expected exception"); } catch (IllegalArgumentException expected) { } try { ByteStreams.slice(supplier, 0, -1); fail("expected exception"); } catch (IllegalArgumentException expected) { } try { ByteStreams.slice(null, 0, 10); fail("expected exception"); } catch (NullPointerException expected) { } sliceHelper(0, 0, 0, 0); sliceHelper(0, 0, 1, 0); sliceHelper(100, 0, 10, 10); sliceHelper(100, 0, 100, 100); sliceHelper(100, 5, 10, 10); sliceHelper(100, 5, 100, 95); sliceHelper(100, 100, 0, 0); sliceHelper(100, 100, 10, 0); try { sliceHelper(100, 101, 10, 0); fail("expected exception"); } catch (EOFException expected) { } } /** * @param input the size of the input stream * @param offset the first argument to {@link ByteStreams#slice} * @param length the second argument to {@link ByteStreams#slice} * @param expectRead the number of bytes we expect to read */ private static void sliceHelper( int input, int offset, long length, int expectRead) throws IOException { Preconditions.checkArgument(expectRead == (int) Math.max(0, Math.min(input, offset + length) - offset)); InputSupplier<? extends InputStream> supplier = ByteStreams.newInputStreamSupplier(newPreFilledByteArray(input)); assertTrue(Arrays.equals( newPreFilledByteArray(offset, expectRead), ByteStreams.toByteArray(ByteStreams.slice(supplier, offset, length)))); } private static InputStream newTestStream(int n) { return new ByteArrayInputStream(newPreFilledByteArray(n)); } private static CheckCloseSupplier.Input<InputStream> newCheckInput( InputSupplier<? extends InputStream> delegate) { return new CheckCloseSupplier.Input<InputStream>(delegate) { @Override protected InputStream wrap(InputStream object, final Callback callback) { return new FilterInputStream(object) { @Override public void close() throws IOException { callback.delegateClosed(); super.close(); } }; } }; } private static CheckCloseSupplier.Output<OutputStream> newCheckOutput( OutputSupplier<? extends OutputStream> delegate) { return new CheckCloseSupplier.Output<OutputStream>(delegate) { @Override protected OutputStream wrap(OutputStream object, final Callback callback) { return new FilterOutputStream(object) { @Override public void close() throws IOException { callback.delegateClosed(); super.close(); } }; } }; } /** Stream that will skip a maximum number of bytes at a time. */ private static class SlowSkipper extends FilterInputStream { private final long max; public SlowSkipper(InputStream in, long max) { super(in); this.max = max; } @Override public long skip(long n) throws IOException { return super.skip(Math.min(max, n)); } } public void testByteProcessorStopEarly() throws IOException { byte[] array = newPreFilledByteArray(6000); assertEquals((Integer) 42, ByteStreams.readBytes(ByteStreams.newInputStreamSupplier(array), new ByteProcessor<Integer>() { @Override public boolean processBytes(byte[] buf, int off, int len) { assertTrue(Arrays.equals( copyOfRange(buf, off, off + len), newPreFilledByteArray(4096))); return false; } @Override public Integer getResult() { return 42; } })); } private static byte[] copyOfRange(byte[] in, int from, int to) { byte[] out = new byte[to - from]; for (int i = 0; i < to - from; i++) { out[i] = in[from + i]; } return out; } private static void assertEquals(byte[] expected, byte[] actual) { assertEquals(Bytes.asList(expected), Bytes.asList(actual)); } }