blob: e697ffa106d510719f8d5a19616257d4d2aabeb5 [file] [log] [blame]
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.archivepatcher.generator;
import com.google.archivepatcher.shared.UnitTestZipArchive;
import com.google.archivepatcher.shared.UnitTestZipEntry;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests for {@link PreDiffExecutor}.
*/
@RunWith(JUnit4.class)
@SuppressWarnings("javadoc")
public class PreDiffExecutorTest {
private static final UnitTestZipEntry ENTRY_LEVEL_6 =
UnitTestZipArchive.makeUnitTestZipEntry("/for/great/justice", 6, "entry A", null);
private static final UnitTestZipEntry ENTRY_LEVEL_9 =
UnitTestZipArchive.makeUnitTestZipEntry("/for/great/justice", 9, "entry A", null);
private List<File> tempFilesCreated;
private File deltaFriendlyOldFile;
private File deltaFriendlyNewFile;
@Before
public void setup() throws IOException {
tempFilesCreated = new LinkedList<File>();
deltaFriendlyOldFile = newTempFile();
deltaFriendlyNewFile = newTempFile();
}
@After
public void tearDown() {
for (File file : tempFilesCreated) {
try {
file.delete();
} catch (Exception ignored) {
// Nothing
}
}
}
/**
* Stores the specified bytes to disk in a temp file and returns the temp file.
* @param data the bytes to store
* @throws IOException if it fails
*/
private File store(byte[] data) throws IOException {
File file = newTempFile();
FileOutputStream out = new FileOutputStream(file);
out.write(data);
out.flush();
out.close();
return file;
}
/**
* Make a new temp file and schedule it for deletion on exit and during teardown.
* @return the file created
* @throws IOException if anything goes wrong
*/
private File newTempFile() throws IOException {
File file = File.createTempFile("pdet", "bin");
tempFilesCreated.add(file);
file.deleteOnExit();
return file;
}
private MinimalZipEntry findEntry(File file, String path) throws IOException {
List<MinimalZipEntry> entries = MinimalZipArchive.listEntries(file);
for (MinimalZipEntry entry : entries) {
if (path.equals(entry.getFileName())) {
return entry;
}
}
Assert.fail("path not found: " + path);
return null; // Never executed
}
private byte[] readFile(File file) throws IOException {
byte[] result = new byte[(int) file.length()];
try (FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis)) {
dis.readFully(result);
}
return result;
}
private void assertFileEquals(File file1, File file2) throws IOException {
Assert.assertEquals(file1.length(), file2.length());
byte[] content1 = readFile(file1);
byte[] content2 = readFile(file2);
Assert.assertArrayEquals(content1, content2);
}
@Test
public void testPrepareForDiffing_OneCompressedEntry_Unchanged() throws IOException {
byte[] bytes = UnitTestZipArchive.makeTestZip(Collections.singletonList(ENTRY_LEVEL_6));
File oldFile = store(bytes);
File newFile = store(bytes);
PreDiffExecutor executor =
new PreDiffExecutor.Builder()
.readingOriginalFiles(oldFile, newFile)
.writingDeltaFriendlyFiles(deltaFriendlyOldFile, deltaFriendlyNewFile)
.build();
PreDiffPlan plan = executor.prepareForDiffing();
Assert.assertNotNull(plan);
// The plan should be to leave everything alone because there is no change.
Assert.assertTrue(plan.getOldFileUncompressionPlan().isEmpty());
Assert.assertTrue(plan.getNewFileUncompressionPlan().isEmpty());
Assert.assertTrue(plan.getDeltaFriendlyNewFileRecompressionPlan().isEmpty());
// Because nothing has changed, the delta-friendly files should be exact matches for the
// original files.
assertFileEquals(oldFile, deltaFriendlyOldFile);
assertFileEquals(newFile, deltaFriendlyNewFile);
}
@Test
public void testPrepareForDiffing_OneCompressedEntry_Changed() throws IOException {
byte[] oldBytes = UnitTestZipArchive.makeTestZip(Collections.singletonList(ENTRY_LEVEL_6));
File oldFile = store(oldBytes);
byte[] newBytes = UnitTestZipArchive.makeTestZip(Collections.singletonList(ENTRY_LEVEL_9));
File newFile = store(newBytes);
PreDiffExecutor executor =
new PreDiffExecutor.Builder()
.readingOriginalFiles(oldFile, newFile)
.writingDeltaFriendlyFiles(deltaFriendlyOldFile, deltaFriendlyNewFile)
.build();
PreDiffPlan plan = executor.prepareForDiffing();
Assert.assertNotNull(plan);
// The plan should be to uncompress the data in both the old and new files.
Assert.assertEquals(1, plan.getOldFileUncompressionPlan().size());
Assert.assertEquals(1, plan.getNewFileUncompressionPlan().size());
Assert.assertEquals(1, plan.getDeltaFriendlyNewFileRecompressionPlan().size());
// The delta-friendly files should be larger than the originals.
Assert.assertTrue(oldFile.length() < deltaFriendlyOldFile.length());
Assert.assertTrue(newFile.length() < deltaFriendlyNewFile.length());
// Nitty-gritty, assert that the file content is exactly what is expected.
// 1. Find the entry in the old file.
// 2. Create a buffer to hold the expected data.
// 3. Copy all the file data that PRECEDES the compressed data into the buffer.
// 4. Copy the UNCOMPRESSED data from the unit test object into the buffer.
// 5. Copy all the file data the FOLLOWS the compressed data into the buffer.
// This should be exactly what is produced. Note that this is not a valid ZIP archive, as the
// offsets and lengths in the zip metadata are no longer tied to the actual data. This is
// normal and expected, since the delta-friendly file is not actually an archive anymore.
{ // Scoping block for sanity
MinimalZipEntry oldEntry = findEntry(oldFile, ENTRY_LEVEL_6.path);
ByteArrayOutputStream expectedDeltaFriendlyOldFileBytes = new ByteArrayOutputStream();
expectedDeltaFriendlyOldFileBytes.write(
oldBytes, 0, (int) oldEntry.getFileOffsetOfCompressedData());
expectedDeltaFriendlyOldFileBytes.write(ENTRY_LEVEL_6.getUncompressedBinaryContent());
int oldRemainderOffset =
(int) (oldEntry.getFileOffsetOfCompressedData() + oldEntry.getCompressedSize());
int oldRemainderLength = oldBytes.length - oldRemainderOffset;
expectedDeltaFriendlyOldFileBytes.write(oldBytes, oldRemainderOffset, oldRemainderLength);
byte[] expectedOld = expectedDeltaFriendlyOldFileBytes.toByteArray();
byte[] actualOld = readFile(deltaFriendlyOldFile);
Assert.assertArrayEquals(expectedOld, actualOld);
}
// Now do the same for the new file and new entry
{ // Scoping block for sanity
MinimalZipEntry newEntry = findEntry(newFile, ENTRY_LEVEL_9.path);
ByteArrayOutputStream expectedDeltaFriendlyNewFileBytes = new ByteArrayOutputStream();
expectedDeltaFriendlyNewFileBytes.write(
newBytes, 0, (int) newEntry.getFileOffsetOfCompressedData());
expectedDeltaFriendlyNewFileBytes.write(ENTRY_LEVEL_9.getUncompressedBinaryContent());
int newRemainderOffset =
(int) (newEntry.getFileOffsetOfCompressedData() + newEntry.getCompressedSize());
int newRemainderLength = newBytes.length - newRemainderOffset;
expectedDeltaFriendlyNewFileBytes.write(newBytes, newRemainderOffset, newRemainderLength);
byte[] expectedNew = expectedDeltaFriendlyNewFileBytes.toByteArray();
byte[] actualNew = readFile(deltaFriendlyNewFile);
Assert.assertArrayEquals(expectedNew, actualNew);
}
}
@Test
public void testPrepareForDiffing_OneCompressedEntry_Changed_Limited() throws IOException {
// Like above, but this time limited by a TotalRecompressionLimiter that will prevent the
// uncompression of the resources.
byte[] oldBytes = UnitTestZipArchive.makeTestZip(Collections.singletonList(ENTRY_LEVEL_6));
File oldFile = store(oldBytes);
byte[] newBytes = UnitTestZipArchive.makeTestZip(Collections.singletonList(ENTRY_LEVEL_9));
File newFile = store(newBytes);
TotalRecompressionLimiter limiter = new TotalRecompressionLimiter(1); // 1 byte limitation
PreDiffExecutor executor =
new PreDiffExecutor.Builder()
.readingOriginalFiles(oldFile, newFile)
.writingDeltaFriendlyFiles(deltaFriendlyOldFile, deltaFriendlyNewFile)
.withRecommendationModifier(limiter)
.build();
PreDiffPlan plan = executor.prepareForDiffing();
Assert.assertNotNull(plan);
// The plan should be to leave everything alone because of the limiter
Assert.assertTrue(plan.getOldFileUncompressionPlan().isEmpty());
Assert.assertTrue(plan.getNewFileUncompressionPlan().isEmpty());
Assert.assertTrue(plan.getDeltaFriendlyNewFileRecompressionPlan().isEmpty());
// Because nothing has changed, the delta-friendly files should be exact matches for the
// original files.
assertFileEquals(oldFile, deltaFriendlyOldFile);
assertFileEquals(newFile, deltaFriendlyNewFile);
}
}