13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/*
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2014 Square, Inc.
33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License.
63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at
73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software
113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and
143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License.
153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage okio;
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException;
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.InputStream;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Arrays;
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Random;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.Deflater;
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.Inflater;
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.InflaterInputStream;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.Test;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertEquals;
28c6bd683320121544811f481709b3fdbcbe9b3866Neil Fullerimport static org.junit.Assert.fail;
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic final class DeflaterSinkTest {
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void deflateWithClose() throws Exception {
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer data = new OkBuffer();
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String original = "They're moving in herds. They do move in herds.";
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    data.writeUtf8(original);
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer sink = new OkBuffer();
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    deflaterSink.write(data, data.size());
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    deflaterSink.close();
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer inflated = inflate(sink);
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(original, inflated.readUtf8(inflated.size()));
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void deflateWithSyncFlush() throws Exception {
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String original = "Yes, yes, yes. That's why we're taking extreme precautions.";
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer data = new OkBuffer();
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    data.writeUtf8(original);
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer sink = new OkBuffer();
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    deflaterSink.write(data, data.size());
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    deflaterSink.flush();
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer inflated = inflate(sink);
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(original, inflated.readUtf8(inflated.size()));
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void deflateWellCompressed() throws IOException {
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String original = repeat('a', 1024 * 1024);
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer data = new OkBuffer();
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    data.writeUtf8(original);
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer sink = new OkBuffer();
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    deflaterSink.write(data, data.size());
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    deflaterSink.close();
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer inflated = inflate(sink);
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(original, inflated.readUtf8(inflated.size()));
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void deflatePoorlyCompressed() throws IOException {
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ByteString original = randomBytes(1024 * 1024);
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer data = new OkBuffer();
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    data.write(original);
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer sink = new OkBuffer();
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    deflaterSink.write(data, data.size());
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    deflaterSink.close();
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer inflated = inflate(sink);
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(original, inflated.readByteString(inflated.size()));
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
78c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
79c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  @Test public void multipleSegmentsWithoutCompression() throws IOException {
80c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    OkBuffer buffer = new OkBuffer();
81c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    Deflater deflater = new Deflater();
82c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    deflater.setLevel(Deflater.NO_COMPRESSION);
83c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    DeflaterSink deflaterSink = new DeflaterSink(buffer, deflater);
84c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    int byteCount = Segment.SIZE * 4;
85c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    deflaterSink.write(new OkBuffer().writeUtf8(repeat('a', byteCount)), byteCount);
86c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    deflaterSink.close();
87c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertEquals(repeat('a', byteCount), inflate(buffer).readUtf8(byteCount));
88c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  }
89c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
90c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  /**
91c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller   * This test deflates a single segment of without compression because that's
92c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller   * the easiest way to force close() to emit a large amount of data to the
93c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller   * underlying sink.
94c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller   */
95c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  @Test public void closeWithExceptionWhenWritingAndClosing() throws IOException {
96c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    MockSink mockSink = new MockSink();
97c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    mockSink.scheduleThrow(0, new IOException("first"));
98c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    mockSink.scheduleThrow(1, new IOException("second"));
99c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    Deflater deflater = new Deflater();
100c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    deflater.setLevel(Deflater.NO_COMPRESSION);
101c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    DeflaterSink deflaterSink = new DeflaterSink(mockSink, deflater);
102c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    deflaterSink.write(new OkBuffer().writeUtf8(repeat('a', Segment.SIZE)), Segment.SIZE);
103c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    try {
104c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      deflaterSink.close();
105c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      fail();
106c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    } catch (IOException expected) {
107c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      assertEquals("first", expected.getMessage());
108c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    }
109c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    mockSink.assertLogContains("close()");
110c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  }
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Uses streaming decompression to inflate {@code deflated}. The input must
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * either be finished or have a trailing sync flush.
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private OkBuffer inflate(OkBuffer deflated) throws IOException {
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    InputStream deflatedIn = deflated.inputStream();
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Inflater inflater = new Inflater();
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    InputStream inflatedIn = new InflaterInputStream(deflatedIn, inflater);
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer result = new OkBuffer();
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    byte[] buffer = new byte[8192];
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    while (!inflater.needsInput() || deflated.size() > 0 || deflatedIn.available() > 0) {
1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      int count = inflatedIn.read(buffer, 0, buffer.length);
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.write(buffer, 0, count);
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return result;
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private ByteString randomBytes(int length) {
1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Random random = new Random(0);
1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    byte[] randomBytes = new byte[length];
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    random.nextBytes(randomBytes);
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return ByteString.of(randomBytes);
1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private String repeat(char c, int count) {
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    char[] array = new char[count];
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Arrays.fill(array, c);
1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new String(array);
1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
142