1/* 2 * Copyright (C) 2014 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package okio; 17 18import java.io.IOException; 19import java.io.InputStream; 20import java.util.zip.Deflater; 21import java.util.zip.Inflater; 22import java.util.zip.InflaterInputStream; 23import org.junit.Test; 24 25import static okio.TestUtil.randomBytes; 26import static okio.TestUtil.repeat; 27import static org.junit.Assert.assertEquals; 28import static org.junit.Assert.fail; 29 30public final class DeflaterSinkTest { 31 @Test public void deflateWithClose() throws Exception { 32 Buffer data = new Buffer(); 33 String original = "They're moving in herds. They do move in herds."; 34 data.writeUtf8(original); 35 Buffer sink = new Buffer(); 36 DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); 37 deflaterSink.write(data, data.size()); 38 deflaterSink.close(); 39 Buffer inflated = inflate(sink); 40 assertEquals(original, inflated.readUtf8()); 41 } 42 43 @Test public void deflateWithSyncFlush() throws Exception { 44 String original = "Yes, yes, yes. That's why we're taking extreme precautions."; 45 Buffer data = new Buffer(); 46 data.writeUtf8(original); 47 Buffer sink = new Buffer(); 48 DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); 49 deflaterSink.write(data, data.size()); 50 deflaterSink.flush(); 51 Buffer inflated = inflate(sink); 52 assertEquals(original, inflated.readUtf8()); 53 } 54 55 @Test public void deflateWellCompressed() throws IOException { 56 String original = repeat('a', 1024 * 1024); 57 Buffer data = new Buffer(); 58 data.writeUtf8(original); 59 Buffer sink = new Buffer(); 60 DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); 61 deflaterSink.write(data, data.size()); 62 deflaterSink.close(); 63 Buffer inflated = inflate(sink); 64 assertEquals(original, inflated.readUtf8()); 65 } 66 67 @Test public void deflatePoorlyCompressed() throws IOException { 68 ByteString original = randomBytes(1024 * 1024); 69 Buffer data = new Buffer(); 70 data.write(original); 71 Buffer sink = new Buffer(); 72 DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); 73 deflaterSink.write(data, data.size()); 74 deflaterSink.close(); 75 Buffer inflated = inflate(sink); 76 assertEquals(original, inflated.readByteString()); 77 } 78 79 @Test public void multipleSegmentsWithoutCompression() throws IOException { 80 Buffer buffer = new Buffer(); 81 Deflater deflater = new Deflater(); 82 deflater.setLevel(Deflater.NO_COMPRESSION); 83 DeflaterSink deflaterSink = new DeflaterSink(buffer, deflater); 84 int byteCount = Segment.SIZE * 4; 85 deflaterSink.write(new Buffer().writeUtf8(repeat('a', byteCount)), byteCount); 86 deflaterSink.close(); 87 assertEquals(repeat('a', byteCount), inflate(buffer).readUtf8(byteCount)); 88 } 89 90 @Test public void deflateIntoNonemptySink() throws Exception { 91 String original = "They're moving in herds. They do move in herds."; 92 93 // Exercise all possible offsets for the outgoing segment. 94 for (int i = 0; i < Segment.SIZE; i++) { 95 Buffer data = new Buffer().writeUtf8(original); 96 Buffer sink = new Buffer().writeUtf8(repeat('a', i)); 97 98 DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); 99 deflaterSink.write(data, data.size()); 100 deflaterSink.close(); 101 102 sink.skip(i); 103 Buffer inflated = inflate(sink); 104 assertEquals(original, inflated.readUtf8()); 105 } 106 } 107 108 /** 109 * This test deflates a single segment of without compression because that's 110 * the easiest way to force close() to emit a large amount of data to the 111 * underlying sink. 112 */ 113 @Test public void closeWithExceptionWhenWritingAndClosing() throws IOException { 114 MockSink mockSink = new MockSink(); 115 mockSink.scheduleThrow(0, new IOException("first")); 116 mockSink.scheduleThrow(1, new IOException("second")); 117 Deflater deflater = new Deflater(); 118 deflater.setLevel(Deflater.NO_COMPRESSION); 119 DeflaterSink deflaterSink = new DeflaterSink(mockSink, deflater); 120 deflaterSink.write(new Buffer().writeUtf8(repeat('a', Segment.SIZE)), Segment.SIZE); 121 try { 122 deflaterSink.close(); 123 fail(); 124 } catch (IOException expected) { 125 assertEquals("first", expected.getMessage()); 126 } 127 mockSink.assertLogContains("close()"); 128 } 129 130 /** 131 * Uses streaming decompression to inflate {@code deflated}. The input must 132 * either be finished or have a trailing sync flush. 133 */ 134 private Buffer inflate(Buffer deflated) throws IOException { 135 InputStream deflatedIn = deflated.inputStream(); 136 Inflater inflater = new Inflater(); 137 InputStream inflatedIn = new InflaterInputStream(deflatedIn, inflater); 138 Buffer result = new Buffer(); 139 byte[] buffer = new byte[8192]; 140 while (!inflater.needsInput() || deflated.size() > 0 || deflatedIn.available() > 0) { 141 int count = inflatedIn.read(buffer, 0, buffer.length); 142 if (count != -1) { 143 result.write(buffer, 0, count); 144 } 145 } 146 return result; 147 } 148} 149