1 // Copyright 2016 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.google.archivepatcher.applier; 16 17 import com.google.archivepatcher.shared.JreDeflateParameters; 18 import com.google.archivepatcher.shared.TypedRange; 19 import com.google.archivepatcher.shared.UnitTestZipArchive; 20 import com.google.archivepatcher.shared.UnitTestZipEntry; 21 22 import org.junit.Assert; 23 import org.junit.Before; 24 import org.junit.Test; 25 import org.junit.runner.RunWith; 26 import org.junit.runners.JUnit4; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.IOException; 30 import java.util.Arrays; 31 import java.util.Collections; 32 33 /** 34 * Tests for {@link PartiallyCompressingOutputStream}. 35 */ 36 @RunWith(JUnit4.class) 37 @SuppressWarnings("javadoc") 38 public class PartiallyCompressingOutputStreamTest { 39 private ByteArrayOutputStream outBuffer; 40 private PartiallyCompressingOutputStream stream; 41 42 // The preamble comes before any compressed bytes in the stream 43 private static byte[] PREAMBLE_BYTES = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 44 45 // First range for compression 46 private static JreDeflateParameters PARAMS1 = JreDeflateParameters.of(1, 0, true); 47 private static UnitTestZipEntry ENTRY1 = 48 UnitTestZipArchive.makeUnitTestZipEntry( 49 "/foo", PARAMS1.level, PARAMS1.nowrap, "foo-level1", null); 50 private static long OFFSET1 = PREAMBLE_BYTES.length; 51 private static long LENGTH1 = ENTRY1.getUncompressedBinaryContent().length; 52 private static TypedRange<JreDeflateParameters> COMPRESS_RANGE_1 = 53 new TypedRange<JreDeflateParameters>(OFFSET1, LENGTH1, PARAMS1); 54 55 private static byte[] GAP1_BYTES = new byte[] {37}; 56 57 // Second range for compression, with a gap in between. Note this changes nowrap and level 58 private static JreDeflateParameters PARAMS2 = JreDeflateParameters.of(6, 0, false); 59 private static UnitTestZipEntry ENTRY2 = 60 UnitTestZipArchive.makeUnitTestZipEntry( 61 "/bar", PARAMS2.level, PARAMS2.nowrap, "bar-level6", null); 62 private static long OFFSET2 = OFFSET1 + LENGTH1 + GAP1_BYTES.length; 63 private static long LENGTH2 = ENTRY2.getUncompressedBinaryContent().length; 64 private static TypedRange<JreDeflateParameters> COMPRESS_RANGE_2 = 65 new TypedRange<JreDeflateParameters>(OFFSET2, LENGTH2, PARAMS2); 66 fuse(byte[]... arrays)67 private byte[] fuse(byte[]... arrays) throws IOException { 68 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 69 for (byte[] array : arrays) { 70 buffer.write(array); 71 } 72 return buffer.toByteArray(); 73 } 74 75 @Before setup()76 public void setup() { 77 outBuffer = new ByteArrayOutputStream(); 78 } 79 80 @Test testWrite_Nothing()81 public void testWrite_Nothing() throws IOException { 82 // Test the case where there are no compression ranges at all and nothing is written. 83 stream = 84 new PartiallyCompressingOutputStream( 85 Collections.<TypedRange<JreDeflateParameters>>emptyList(), outBuffer, 32768); 86 byte[] input = new byte[] {}; 87 stream.write(input); 88 stream.flush(); 89 stream.close(); 90 Assert.assertArrayEquals(input, outBuffer.toByteArray()); 91 } 92 93 @Test testWrite_NoneCompressed()94 public void testWrite_NoneCompressed() throws IOException { 95 // Test the case where there are no compression ranges at all. 96 stream = 97 new PartiallyCompressingOutputStream( 98 Collections.<TypedRange<JreDeflateParameters>>emptyList(), outBuffer, 32768); 99 byte[] input = new byte[] {1, 77, 66, 44, 22, 11}; 100 byte[] expected = input.clone(); 101 stream.write(input); 102 stream.flush(); 103 Assert.assertArrayEquals(expected, outBuffer.toByteArray()); 104 } 105 106 @Test testWrite_AllCompressed()107 public void testWrite_AllCompressed() throws IOException { 108 // Test the case where a single compression range covers the entire input 109 TypedRange<JreDeflateParameters> range = 110 new TypedRange<JreDeflateParameters>( 111 0, ENTRY1.getUncompressedBinaryContent().length, PARAMS1); 112 stream = 113 new PartiallyCompressingOutputStream(Collections.singletonList(range), outBuffer, 32768); 114 stream.write(ENTRY1.getUncompressedBinaryContent()); 115 stream.flush(); 116 Assert.assertArrayEquals(ENTRY1.getCompressedBinaryContent(), outBuffer.toByteArray()); 117 } 118 119 @Test testWrite_GapAndCompression()120 public void testWrite_GapAndCompression() throws IOException { 121 // Write uncompressed data followed by compressed data 122 stream = 123 new PartiallyCompressingOutputStream( 124 Collections.singletonList(COMPRESS_RANGE_1), outBuffer, 32768); 125 byte[] input = fuse(PREAMBLE_BYTES, ENTRY1.getUncompressedBinaryContent()); 126 byte[] expected = fuse(PREAMBLE_BYTES, ENTRY1.getCompressedBinaryContent()); 127 stream.write(input); 128 stream.flush(); 129 Assert.assertArrayEquals(expected, outBuffer.toByteArray()); 130 } 131 132 @Test testWrite_GapAndCompressionAndGap()133 public void testWrite_GapAndCompressionAndGap() throws IOException { 134 // Write uncompressed data followed by compressed data and another bit of uncompressed data 135 stream = 136 new PartiallyCompressingOutputStream( 137 Collections.singletonList(COMPRESS_RANGE_1), outBuffer, 32768); 138 byte[] input = fuse(PREAMBLE_BYTES, ENTRY1.getUncompressedBinaryContent(), GAP1_BYTES); 139 byte[] expected = fuse(PREAMBLE_BYTES, ENTRY1.getCompressedBinaryContent(), GAP1_BYTES); 140 stream.write(input); 141 stream.flush(); 142 Assert.assertArrayEquals(expected, outBuffer.toByteArray()); 143 } 144 145 @Test testWrite_MixedSequence_Thrash()146 public void testWrite_MixedSequence_Thrash() throws IOException { 147 // Write uncompressed data, compressed data, uncompressed data, compressed data (new params) 148 // Thrash by writing one byte at a time to pound on edge-casey code 149 stream = 150 new PartiallyCompressingOutputStream( 151 Arrays.asList(COMPRESS_RANGE_1, COMPRESS_RANGE_2), outBuffer, 32768); 152 byte[] input = 153 fuse( 154 PREAMBLE_BYTES, 155 ENTRY1.getUncompressedBinaryContent(), 156 GAP1_BYTES, 157 ENTRY2.getUncompressedBinaryContent()); 158 byte[] expected = 159 fuse( 160 PREAMBLE_BYTES, 161 ENTRY1.getCompressedBinaryContent(), 162 GAP1_BYTES, 163 ENTRY2.getCompressedBinaryContent()); 164 for (int x = 0; x < input.length; x++) { 165 stream.write(input[x] & 0xff); 166 stream.flush(); 167 } 168 stream.close(); 169 Assert.assertArrayEquals(expected, outBuffer.toByteArray()); 170 } 171 } 172