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.generator; 16 17 import com.google.archivepatcher.shared.JreDeflateParameters; 18 import com.google.archivepatcher.shared.PatchConstants; 19 import com.google.archivepatcher.shared.TypedRange; 20 21 import org.junit.After; 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.ByteArrayInputStream; 29 import java.io.ByteArrayOutputStream; 30 import java.io.DataInputStream; 31 import java.io.File; 32 import java.io.FileOutputStream; 33 import java.io.IOException; 34 import java.util.Collections; 35 import java.util.List; 36 37 /** 38 * Tests for {@link PatchWriter}. 39 */ 40 @RunWith(JUnit4.class) 41 @SuppressWarnings("javadoc") 42 public class PatchWriterTest { 43 // This is Integer.MAX_VALUE + 1. 44 private static final long BIG = 2048L * 1024L * 1024L; 45 46 private static final JreDeflateParameters DEFLATE_PARAMS = JreDeflateParameters.of(6, 0, true); 47 48 private static final TypedRange<Void> OLD_DELTA_FRIENDLY_UNCOMPRESS_RANGE = 49 new TypedRange<Void>(BIG, 17L, null); 50 51 private static final TypedRange<JreDeflateParameters> NEW_DELTA_FRIENDLY_UNCOMPRESS_RANGE = 52 new TypedRange<JreDeflateParameters>(BIG - 100L, BIG, DEFLATE_PARAMS); 53 54 private static final TypedRange<JreDeflateParameters> NEW_DELTA_FRIENDLY_RECOMPRESS_RANGE = 55 new TypedRange<JreDeflateParameters>(BIG, BIG, DEFLATE_PARAMS); 56 57 private static final List<TypedRange<Void>> OLD_DELTA_FRIENDLY_UNCOMPRESS_PLAN = 58 Collections.singletonList(OLD_DELTA_FRIENDLY_UNCOMPRESS_RANGE); 59 60 private static final List<TypedRange<JreDeflateParameters>> NEW_DELTA_FRIENDLY_UNCOMPRESS_PLAN = 61 Collections.singletonList(NEW_DELTA_FRIENDLY_UNCOMPRESS_RANGE); 62 63 private static final List<TypedRange<JreDeflateParameters>> NEW_DELTA_FRIENDLY_RECOMPRESS_PLAN = 64 Collections.singletonList(NEW_DELTA_FRIENDLY_RECOMPRESS_RANGE); 65 66 private static final long DELTA_FRIENDLY_OLD_FILE_SIZE = BIG - 75L; 67 68 private static final long DELTA_FRIENDLY_NEW_FILE_SIZE = BIG + 75L; 69 70 private static final PreDiffPlan PLAN = 71 new PreDiffPlan( 72 Collections.<QualifiedRecommendation>emptyList(), 73 OLD_DELTA_FRIENDLY_UNCOMPRESS_PLAN, 74 NEW_DELTA_FRIENDLY_UNCOMPRESS_PLAN, 75 NEW_DELTA_FRIENDLY_RECOMPRESS_PLAN); 76 77 private static final String DELTA_CONTENT = "this is a really cool delta, woo"; 78 79 private File deltaFile = null; 80 81 private ByteArrayOutputStream buffer = null; 82 83 @Before setup()84 public void setup() throws IOException { 85 buffer = new ByteArrayOutputStream(); 86 deltaFile = File.createTempFile("patchwritertest", "delta"); 87 deltaFile.deleteOnExit(); 88 try (FileOutputStream out = new FileOutputStream(deltaFile)) { 89 out.write(DELTA_CONTENT.getBytes()); 90 out.flush(); 91 } 92 } 93 94 @After tearDown()95 public void tearDown() { 96 deltaFile.delete(); 97 } 98 99 @Test testWriteV1Patch()100 public void testWriteV1Patch() throws IOException { 101 // --------------------------------------------------------------------------------------------- 102 // CAUTION - DO NOT CHANGE THIS FUNCTION WITHOUT DUE CONSIDERATION FOR BREAKING THE PATCH FORMAT 103 // --------------------------------------------------------------------------------------------- 104 // This test writes a simple patch with all the static data listed above and verifies it. 105 // This code MUST be INDEPENDENT of the real patch parser code, even if it is partially 106 // redundant; this guards against accidental changes to the patch writer that could alter the 107 // format and otherwise escape detection. 108 PatchWriter writer = 109 new PatchWriter( 110 PLAN, DELTA_FRIENDLY_OLD_FILE_SIZE, DELTA_FRIENDLY_NEW_FILE_SIZE, deltaFile); 111 writer.writeV1Patch(buffer); 112 DataInputStream patchIn = new DataInputStream(new ByteArrayInputStream(buffer.toByteArray())); 113 byte[] eightBytes = new byte[8]; 114 115 // Start by reading the signature and flags 116 patchIn.readFully(eightBytes); 117 Assert.assertArrayEquals(PatchConstants.IDENTIFIER.getBytes("US-ASCII"), eightBytes); 118 Assert.assertEquals(0, patchIn.readInt()); // Flags, all reserved in v1 119 120 Assert.assertEquals(DELTA_FRIENDLY_OLD_FILE_SIZE, patchIn.readLong()); 121 122 // Read the uncompression instructions 123 Assert.assertEquals(1, patchIn.readInt()); // Number of old archive uncompression instructions 124 Assert.assertEquals(OLD_DELTA_FRIENDLY_UNCOMPRESS_RANGE.getOffset(), patchIn.readLong()); 125 Assert.assertEquals(OLD_DELTA_FRIENDLY_UNCOMPRESS_RANGE.getLength(), patchIn.readLong()); 126 127 // Read the recompression instructions 128 Assert.assertEquals(1, patchIn.readInt()); // Number of new archive recompression instructions 129 Assert.assertEquals(NEW_DELTA_FRIENDLY_RECOMPRESS_RANGE.getOffset(), patchIn.readLong()); 130 Assert.assertEquals(NEW_DELTA_FRIENDLY_RECOMPRESS_RANGE.getLength(), patchIn.readLong()); 131 // Now the JreDeflateParameters for the record 132 Assert.assertEquals( 133 PatchConstants.CompatibilityWindowId.DEFAULT_DEFLATE.patchValue, patchIn.read()); 134 Assert.assertEquals(DEFLATE_PARAMS.level, patchIn.read()); 135 Assert.assertEquals(DEFLATE_PARAMS.strategy, patchIn.read()); 136 Assert.assertEquals(DEFLATE_PARAMS.nowrap ? 1 : 0, patchIn.read()); 137 138 // Delta section. V1 patches have exactly one delta entry and it is always mapped to the entire 139 // file contents of the delta-friendly archives 140 Assert.assertEquals(1, patchIn.readInt()); // Number of difference records 141 Assert.assertEquals(PatchConstants.DeltaFormat.BSDIFF.patchValue, patchIn.read()); 142 Assert.assertEquals(0, patchIn.readLong()); // Old delta-friendly range start 143 Assert.assertEquals(DELTA_FRIENDLY_OLD_FILE_SIZE, patchIn.readLong()); // old range length 144 Assert.assertEquals(0, patchIn.readLong()); // New delta-friendly range start 145 Assert.assertEquals(DELTA_FRIENDLY_NEW_FILE_SIZE, patchIn.readLong()); // new range length 146 byte[] expectedDeltaContent = DELTA_CONTENT.getBytes("US-ASCII"); 147 Assert.assertEquals(expectedDeltaContent.length, patchIn.readLong()); 148 byte[] actualDeltaContent = new byte[expectedDeltaContent.length]; 149 patchIn.readFully(actualDeltaContent); 150 Assert.assertArrayEquals(expectedDeltaContent, actualDeltaContent); 151 } 152 } 153