diff options
Diffstat (limited to 'generator/src/test/java/com/google/archivepatcher/generator/DeltaFriendlyOldBlobSizeLimiterTest.java')
-rw-r--r-- | generator/src/test/java/com/google/archivepatcher/generator/DeltaFriendlyOldBlobSizeLimiterTest.java | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/generator/src/test/java/com/google/archivepatcher/generator/DeltaFriendlyOldBlobSizeLimiterTest.java b/generator/src/test/java/com/google/archivepatcher/generator/DeltaFriendlyOldBlobSizeLimiterTest.java new file mode 100644 index 0000000..0bbacaf --- /dev/null +++ b/generator/src/test/java/com/google/archivepatcher/generator/DeltaFriendlyOldBlobSizeLimiterTest.java @@ -0,0 +1,316 @@ +// 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 java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +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 DeltaFriendlyOldBlobSizeLimiter}. */ +@RunWith(JUnit4.class) +@SuppressWarnings("javadoc") +public class DeltaFriendlyOldBlobSizeLimiterTest { + private static final int DEFLATE_COMPRESSION_METHOD = 8; + + private static final MinimalZipEntry UNIMPORTANT = makeFakeEntry("/unimportant", 1337, 1337); + private static final MinimalZipEntry ENTRY_A_100K = + makeFakeEntry("/a/100k", 100 * 1024, 200 * 1024); + private static final MinimalZipEntry ENTRY_B_200K = + makeFakeEntry("/b/200k", 100 * 1024, 300 * 1024); + private static final MinimalZipEntry ENTRY_C_300K = + makeFakeEntry("/c/300k", 100 * 1024, 400 * 1024); + private static final MinimalZipEntry ENTRY_D_400K = + makeFakeEntry("/d/400k", 100 * 1024, 500 * 1024); + private static final MinimalZipEntry IGNORED_A = makeFakeEntry("/ignored/a", 1234, 5678); + private static final MinimalZipEntry IGNORED_B = makeFakeEntry("/ignored/b", 5678, 9101112); + private static final MinimalZipEntry IGNORED_C = makeFakeEntry("/ignored/c", 9101112, 13141516); + + // First four recommendations are all ones where uncompression of the old resource is required. + // Note that there is a mix of UNCOMPRESS_OLD and UNCOMPRESS_BOTH, both of which will have the + // "old" entry flagged for uncompression (i.e., should be relevant to the filtering logic). + private static final QualifiedRecommendation REC_A_100K = + new QualifiedRecommendation( + ENTRY_A_100K, + UNIMPORTANT, + Recommendation.UNCOMPRESS_BOTH, + RecommendationReason.COMPRESSED_BYTES_CHANGED); + private static final QualifiedRecommendation REC_B_200K = + new QualifiedRecommendation( + ENTRY_B_200K, + UNIMPORTANT, + Recommendation.UNCOMPRESS_OLD, + RecommendationReason.UNCOMPRESSED_CHANGED_TO_COMPRESSED); + private static final QualifiedRecommendation REC_C_300K = + new QualifiedRecommendation( + ENTRY_C_300K, + UNIMPORTANT, + Recommendation.UNCOMPRESS_BOTH, + RecommendationReason.COMPRESSED_BYTES_CHANGED); + private static final QualifiedRecommendation REC_D_400K = + new QualifiedRecommendation( + ENTRY_D_400K, + UNIMPORTANT, + Recommendation.UNCOMPRESS_BOTH, + RecommendationReason.COMPRESSED_CHANGED_TO_UNCOMPRESSED); + + // Remaining recommendations are all ones where recompression is NOT required. Note the mixture of + // UNCOMPRESS_NEITHER and UNCOMPRESS_OLD, neither of which will have the "new" entry flagged for + // recompression (ie., must be ignored by the filtering logic). + private static final QualifiedRecommendation REC_IGNORED_A_UNCHANGED = + new QualifiedRecommendation( + IGNORED_A, + UNIMPORTANT, + Recommendation.UNCOMPRESS_NEITHER, + RecommendationReason.COMPRESSED_BYTES_IDENTICAL); + private static final QualifiedRecommendation REC_IGNORED_B_BOTH_UNCOMPRESSED = + new QualifiedRecommendation( + IGNORED_B, + UNIMPORTANT, + Recommendation.UNCOMPRESS_NEITHER, + RecommendationReason.BOTH_ENTRIES_UNCOMPRESSED); + private static final QualifiedRecommendation REC_IGNORED_C_UNSUITABLE = + new QualifiedRecommendation( + IGNORED_C, + UNIMPORTANT, + Recommendation.UNCOMPRESS_NEITHER, + RecommendationReason.UNSUITABLE); + + /** Convenience reference to all the recommendations that should be ignored by filtering. */ + private static final List<QualifiedRecommendation> ALL_IGNORED_RECS = + Collections.unmodifiableList( + Arrays.asList( + REC_IGNORED_A_UNCHANGED, REC_IGNORED_B_BOTH_UNCOMPRESSED, REC_IGNORED_C_UNSUITABLE)); + + /** Convenience reference to all the recommendations that are subject to filtering. */ + private static final List<QualifiedRecommendation> ALL_RECS = + Collections.unmodifiableList( + Arrays.asList( + REC_IGNORED_A_UNCHANGED, + REC_A_100K, + REC_IGNORED_B_BOTH_UNCOMPRESSED, + REC_D_400K, + REC_IGNORED_C_UNSUITABLE, + REC_B_200K, + REC_C_300K)); + + /** + * Make a structurally valid but totally bogus {@link MinimalZipEntry} for the purpose of testing + * the {@link RecommendationModifier}. + * + * @param path the path to set on the entry, to help with debugging + * @param compressedSize the compressed size of the entry, in bytes + * @param uncompressedSize the uncompressed size of the entry, in bytes + */ + private static MinimalZipEntry makeFakeEntry( + String path, long compressedSize, long uncompressedSize) { + try { + return new MinimalZipEntry( + DEFLATE_COMPRESSION_METHOD, // == deflate + 0, // crc32OfUncompressedData (ignored for this test) + compressedSize, + uncompressedSize, + path.getBytes("UTF8"), + true, // generalPurposeFlagBit11 (true=UTF8) + 0 // fileOffsetOfLocalEntry (ignored for this test) + ); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); // Impossible on any modern system + } + } + + @Test + public void testNegativeLimit() { + try { + new DeltaFriendlyOldBlobSizeLimiter(-1); + Assert.fail("Set a negative limit"); + } catch (IllegalArgumentException expected) { + // Pass + } + } + + /** + * Asserts that the two collections contain exactly the same elements. This isn't as rigorous as + * it should be, but is ok for this test scenario. Checks the contents but not the iteration order + * of the collections handed in. + */ + private static <T> void assertEquivalence(Collection<T> c1, Collection<T> c2) { + String errorMessage = "Expected " + c1 + " but was " + c2; + Assert.assertEquals(errorMessage, c1.size(), c2.size()); + Assert.assertTrue(errorMessage, c1.containsAll(c2)); + Assert.assertTrue(errorMessage, c2.containsAll(c1)); + } + + /** + * Given {@link QualifiedRecommendation}s, manufacture equivalents altered in the way that the + * {@link DeltaFriendlyOldBlobSizeLimiter} would. + * + * @param originals the original recommendations + * @return the altered recommendations + */ + private static final List<QualifiedRecommendation> suppressed( + QualifiedRecommendation... originals) { + List<QualifiedRecommendation> result = new ArrayList<>(originals.length); + for (QualifiedRecommendation original : originals) { + result.add( + new QualifiedRecommendation( + original.getOldEntry(), + original.getNewEntry(), + Recommendation.UNCOMPRESS_NEITHER, + RecommendationReason.RESOURCE_CONSTRAINED)); + } + return result; + } + + private File tempFile = null; + + @Before + public void setup() throws IOException { + // Make an empty file to test the recommender's limitation logic + tempFile = File.createTempFile("DeltaFriendlyOldBlobSizeLimiterTest", "test"); + tempFile.deleteOnExit(); + } + + @After + public void tearDown() { + tempFile.delete(); + } + + @Test + public void testZeroLimit() { + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(0); + List<QualifiedRecommendation> expected = new ArrayList<QualifiedRecommendation>(); + expected.addAll(suppressed(REC_A_100K, REC_B_200K, REC_C_300K, REC_D_400K)); + expected.addAll(ALL_IGNORED_RECS); + assertEquivalence(expected, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } + + @Test + public void testMaxLimit() { + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(Long.MAX_VALUE); + assertEquivalence(ALL_RECS, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } + + @Test + public void testLimit_ExactlySmallest() { + long limit = + REC_A_100K.getOldEntry().getUncompressedSize() + - REC_A_100K.getOldEntry().getCompressedSize(); // Exactly large enough + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(limit); + List<QualifiedRecommendation> expected = new ArrayList<QualifiedRecommendation>(); + expected.add(REC_A_100K); + expected.addAll(suppressed(REC_B_200K, REC_C_300K, REC_D_400K)); + expected.addAll(ALL_IGNORED_RECS); + assertEquivalence(expected, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } + + @Test + public void testLimit_EdgeUnderSmallest() { + long limit = + REC_A_100K.getOldEntry().getUncompressedSize() + - REC_A_100K.getOldEntry().getCompressedSize() + - 1; // 1 byte too small + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(limit); + List<QualifiedRecommendation> expected = new ArrayList<QualifiedRecommendation>(); + expected.addAll(suppressed(REC_A_100K, REC_B_200K, REC_C_300K, REC_D_400K)); + expected.addAll(ALL_IGNORED_RECS); + assertEquivalence(expected, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } + + @Test + public void testLimit_EdgeOverSmallest() { + long limit = + REC_A_100K.getOldEntry().getUncompressedSize() + - REC_A_100K.getOldEntry().getCompressedSize() + + 1; // 1 byte extra room + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(limit); + List<QualifiedRecommendation> expected = new ArrayList<QualifiedRecommendation>(); + expected.add(REC_A_100K); + expected.addAll(suppressed(REC_B_200K, REC_C_300K, REC_D_400K)); + expected.addAll(ALL_IGNORED_RECS); + assertEquivalence(expected, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } + + @Test + public void testLimit_ExactlyLargest() { + long limit = + REC_D_400K.getOldEntry().getUncompressedSize() + - REC_D_400K.getOldEntry().getCompressedSize(); // Exactly large enough + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(limit); + List<QualifiedRecommendation> expected = new ArrayList<QualifiedRecommendation>(); + expected.add(REC_D_400K); + expected.addAll(suppressed(REC_A_100K, REC_B_200K, REC_C_300K)); + expected.addAll(ALL_IGNORED_RECS); + assertEquivalence(expected, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } + + @Test + public void testLimit_EdgeUnderLargest() { + long limit = + REC_D_400K.getOldEntry().getUncompressedSize() + - REC_D_400K.getOldEntry().getCompressedSize() + - 1; // 1 byte too small + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(limit); + List<QualifiedRecommendation> expected = new ArrayList<QualifiedRecommendation>(); + expected.add(REC_C_300K); + expected.addAll(suppressed(REC_A_100K, REC_B_200K, REC_D_400K)); + expected.addAll(ALL_IGNORED_RECS); + assertEquivalence(expected, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } + + @Test + public void testLimit_EdgeOverLargest() { + long limit = + REC_D_400K.getOldEntry().getUncompressedSize() + - REC_D_400K.getOldEntry().getCompressedSize() + + 1; // 1 byte extra room + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(limit); + List<QualifiedRecommendation> expected = new ArrayList<QualifiedRecommendation>(); + expected.add(REC_D_400K); + expected.addAll(suppressed(REC_A_100K, REC_B_200K, REC_C_300K)); + expected.addAll(ALL_IGNORED_RECS); + assertEquivalence(expected, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } + + @Test + public void testLimit_Complex() { + // A more nuanced test. Here we set up a limit of 600k - big enough to get the largest and the + // THIRD largest files. The second largest will fail because there isn't enough space after + // adding the first largest, and the fourth largest will fail because there is not enough space + // after adding the third largest. Tricky. + long limit = + (REC_D_400K.getOldEntry().getUncompressedSize() + - REC_D_400K.getOldEntry().getCompressedSize()) + + (REC_B_200K.getOldEntry().getUncompressedSize() + - REC_B_200K.getOldEntry().getCompressedSize()); + DeltaFriendlyOldBlobSizeLimiter limiter = new DeltaFriendlyOldBlobSizeLimiter(limit); + List<QualifiedRecommendation> expected = new ArrayList<QualifiedRecommendation>(); + expected.add(REC_B_200K); + expected.add(REC_D_400K); + expected.addAll(suppressed(REC_A_100K, REC_C_300K)); + expected.addAll(ALL_IGNORED_RECS); + assertEquivalence(expected, limiter.getModifiedRecommendations(tempFile, tempFile, ALL_RECS)); + } +} |