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