diff options
Diffstat (limited to 'okio/src/jvmTest/java/okio/InflaterSourceTest.java')
-rw-r--r-- | okio/src/jvmTest/java/okio/InflaterSourceTest.java | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/okio/src/jvmTest/java/okio/InflaterSourceTest.java b/okio/src/jvmTest/java/okio/InflaterSourceTest.java new file mode 100644 index 00000000..0486638d --- /dev/null +++ b/okio/src/jvmTest/java/okio/InflaterSourceTest.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * 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 okio; + +import java.io.EOFException; +import java.io.IOException; +import java.util.List; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Inflater; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import static kotlin.text.StringsKt.repeat; +import static okio.TestUtil.SEGMENT_SIZE; +import static okio.TestUtil.randomBytes; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; + +@RunWith(Parameterized.class) +public final class InflaterSourceTest { + /** + * Use a parameterized test to control how many bytes the InflaterSource gets with each request + * for more bytes. + */ + @Parameters(name = "{0}") + public static List<Object[]> parameters() { + return BufferedSourceFactory.Companion.getPARAMETERIZED_TEST_VALUES(); + } + + public final BufferedSourceFactory bufferFactory; + public BufferedSink deflatedSink; + public BufferedSource deflatedSource; + + public InflaterSourceTest(BufferedSourceFactory bufferFactory) { + this.bufferFactory = bufferFactory; + resetDeflatedSourceAndSink(); + } + + private void resetDeflatedSourceAndSink() { + BufferedSourceFactory.Pipe pipe = bufferFactory.pipe(); + this.deflatedSink = pipe.getSink(); + this.deflatedSource = pipe.getSource(); + } + + @Test public void inflate() throws Exception { + decodeBase64("eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tKtYDAF6CD5s="); + Buffer inflated = inflate(deflatedSource); + assertEquals("God help us, we're in the hands of engineers.", inflated.readUtf8()); + } + + @Test public void inflateTruncated() throws Exception { + decodeBase64("eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tKtYDAF6CDw=="); + try { + inflate(deflatedSource); + fail(); + } catch (EOFException expected) { + } + } + + @Test public void inflateWellCompressed() throws Exception { + decodeBase64("eJztwTEBAAAAwqCs61/CEL5AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8BtFeWvE="); + String original = repeat("a", 1024 * 1024); + deflate(ByteString.encodeUtf8(original)); + Buffer inflated = inflate(deflatedSource); + assertEquals(original, inflated.readUtf8()); + } + + @Test public void inflatePoorlyCompressed() throws Exception { + assumeFalse(bufferFactory.isOneByteAtATime()); // 8 GiB for 1 byte per segment! + + ByteString original = randomBytes(1024 * 1024); + deflate(original); + Buffer inflated = inflate(deflatedSource); + assertEquals(original, inflated.readByteString()); + } + + @Test public void inflateIntoNonemptySink() throws Exception { + for (int i = 0; i < SEGMENT_SIZE; i++) { + resetDeflatedSourceAndSink(); + Buffer inflated = new Buffer().writeUtf8(repeat("a", i)); + deflate(ByteString.encodeUtf8("God help us, we're in the hands of engineers.")); + InflaterSource source = new InflaterSource(deflatedSource, new Inflater()); + while (source.read(inflated, Integer.MAX_VALUE) != -1) { + } + inflated.skip(i); + assertEquals("God help us, we're in the hands of engineers.", inflated.readUtf8()); + } + } + + @Test public void inflateSingleByte() throws Exception { + Buffer inflated = new Buffer(); + decodeBase64("eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tKtYDAF6CD5s="); + InflaterSource source = new InflaterSource(deflatedSource, new Inflater()); + source.read(inflated, 1); + source.close(); + assertEquals("G", inflated.readUtf8()); + assertEquals(0, inflated.size()); + } + + @Test public void inflateByteCount() throws Exception { + assumeFalse(bufferFactory.isOneByteAtATime()); // This test assumes one step. + + Buffer inflated = new Buffer(); + decodeBase64("eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tKtYDAF6CD5s="); + InflaterSource source = new InflaterSource(deflatedSource, new Inflater()); + source.read(inflated, 11); + source.close(); + assertEquals("God help us", inflated.readUtf8()); + assertEquals(0, inflated.size()); + } + + @Test public void sourceExhaustedPrematurelyOnRead() throws Exception { + // Deflate 0 bytes of data that lacks the in-stream terminator. + decodeBase64("eJwAAAD//w=="); + + Buffer inflated = new Buffer(); + Inflater inflater = new Inflater(); + InflaterSource source = new InflaterSource(deflatedSource, inflater); + assertThat(deflatedSource.exhausted()).isFalse(); + try { + source.read(inflated, Long.MAX_VALUE); + fail(); + } catch (EOFException expected) { + assertThat(expected).hasMessage("source exhausted prematurely"); + } + + // Despite the exception, the read() call made forward progress on the underlying stream! + assertThat(deflatedSource.exhausted()).isTrue(); + } + + /** + * Confirm that {@link InflaterSource#readOrInflate} consumes a byte on each call even if it + * doesn't produce a byte on every call. + */ + @Test public void readOrInflateMakesByteByByteProgress() throws Exception { + // Deflate 0 bytes of data that lacks the in-stream terminator. + decodeBase64("eJwAAAD//w=="); + int deflatedByteCount = 7; + + Buffer inflated = new Buffer(); + Inflater inflater = new Inflater(); + InflaterSource source = new InflaterSource(deflatedSource, inflater); + assertThat(deflatedSource.exhausted()).isFalse(); + + if (bufferFactory.isOneByteAtATime()) { + for (int i = 0; i < deflatedByteCount; i++) { + assertThat(inflater.getBytesRead()).isEqualTo(i); + assertThat(source.readOrInflate(inflated, Long.MAX_VALUE)).isEqualTo(0L); + } + } else { + assertThat(source.readOrInflate(inflated, Long.MAX_VALUE)).isEqualTo(0L); + } + + assertThat(inflater.getBytesRead()).isEqualTo(deflatedByteCount); + assertThat(deflatedSource.exhausted()); + } + + private void decodeBase64(String s) throws IOException { + deflatedSink.write(ByteString.decodeBase64(s)); + deflatedSink.flush(); + } + + /** Use DeflaterOutputStream to deflate source. */ + private void deflate(ByteString source) throws IOException { + Sink sink = Okio.sink(new DeflaterOutputStream(deflatedSink.outputStream())); + sink.write(new Buffer().write(source), source.size()); + sink.close(); + } + + /** Returns a new buffer containing the inflated contents of {@code deflated}. */ + private Buffer inflate(BufferedSource deflated) throws IOException { + Buffer result = new Buffer(); + InflaterSource source = new InflaterSource(deflated, new Inflater()); + while (source.read(result, Integer.MAX_VALUE) != -1) { + } + return result; + } +} |