summaryrefslogtreecommitdiff
path: root/src/test/java/com/android/tools/build/apkzlib/zip/FileUseMapTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/android/tools/build/apkzlib/zip/FileUseMapTest.java')
-rw-r--r--src/test/java/com/android/tools/build/apkzlib/zip/FileUseMapTest.java132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/test/java/com/android/tools/build/apkzlib/zip/FileUseMapTest.java b/src/test/java/com/android/tools/build/apkzlib/zip/FileUseMapTest.java
new file mode 100644
index 0000000..363a993
--- /dev/null
+++ b/src/test/java/com/android/tools/build/apkzlib/zip/FileUseMapTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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.android.tools.build.apkzlib.zip;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Stopwatch;
+import java.text.DecimalFormat;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Tests for {@link FileUseMap}.
+ */
+public class FileUseMapTest {
+
+ /**
+ * Verifies that as elements are added to the map, the performance of adding new elements
+ * is not significantly downgraded. This test creates a map and does several runs until
+ * a maximum is reached or a time limit is reached.
+ *
+ * <p>In each run, a random block is requested from the map with a random alignment and offset.
+ * The time for each run is saved.
+ *
+ * <p>After all runs are completed, the average time of the first runs (the head time) and
+ * the average time of the last runs (the tail time) is computed, as well as the average
+ * time.
+ *
+ * <p>The test passes if the average tail set time is (1) at most twice as long as the average
+ * and (2) is at most three times as long as the head set. This ensures that performance can
+ * degrade somewhat as the file map size increases, but not too much.
+ */
+ @Test
+ @Ignore("This test relies on magic ratios to detect when performance is bad.")
+ public void addPerformanceTest() {
+ final long MAP_SIZE = 10000000;
+ final int MAX_RUNS = 10000;
+ final long MAX_TEST_DURATION_MS = 1000;
+ final int MAX_RANDOM_BLOCK_SIZE = 1000;
+ final int MAX_RANDOM_ALIGNMENT = 10;
+ final int HEAD_SET_SIZE = 1000;
+ final int TAIL_SET_SIZE = 1000;
+ final double MAX_TAIL_HEAD_RATIO = 3.0;
+ final double MAX_TAIL_TOTAL_RATIO = 2.0;
+
+ long mapSize = MAP_SIZE;
+ FileUseMap map = new FileUseMap(mapSize, 0);
+ Random rand = new Random(0);
+
+ long[] runs = new long[MAX_RUNS];
+ int currentRun = 0;
+
+ Stopwatch testStopwatch = Stopwatch.createStarted();
+ while (testStopwatch.elapsed(TimeUnit.MILLISECONDS) < MAX_TEST_DURATION_MS
+ && currentRun < runs.length) {
+ Stopwatch runStopwatch = Stopwatch.createStarted();
+
+ long blockSize = 1 + rand.nextInt(MAX_RANDOM_BLOCK_SIZE);
+ long start = map.locateFree(blockSize, rand.nextInt(MAX_RANDOM_ALIGNMENT),
+ rand.nextInt(MAX_RANDOM_ALIGNMENT), FileUseMap.PositionAlgorithm.BEST_FIT);
+ long end = start + blockSize;
+ if (end >= mapSize) {
+ mapSize *= 2;
+ map.extend(mapSize);
+ }
+
+ map.add(start, end, new Object());
+
+ runs[currentRun] = runStopwatch.elapsed(TimeUnit.NANOSECONDS);
+ currentRun++;
+ }
+
+ double initialAvg = 0;
+ for (int i = 0; i < HEAD_SET_SIZE; i++) {
+ initialAvg += runs[i];
+ }
+
+ initialAvg /= HEAD_SET_SIZE;
+
+ double endAvg = 0;
+ for (int i = currentRun - TAIL_SET_SIZE; i < currentRun; i++) {
+ endAvg += runs[i];
+ }
+
+ endAvg /= TAIL_SET_SIZE;
+
+ double totalAvg = 0;
+ for (int i = 0; i < runs.length; i++) {
+ totalAvg += runs[i];
+ }
+
+ totalAvg /= currentRun;
+
+ if (endAvg > totalAvg * MAX_TAIL_TOTAL_RATIO || endAvg > initialAvg * MAX_TAIL_HEAD_RATIO) {
+ DecimalFormat df = new DecimalFormat("#,###");
+
+ fail("Add performance at end is too bad. Performance in the beginning is "
+ + df.format(initialAvg) + "ns per insertion and at the end is "
+ + df.format(endAvg) + "ns. Average over the total of " + currentRun + " runs "
+ + "is " + df.format(totalAvg) + "ns.");
+ }
+ }
+
+ @Test
+ public void testSizeComputation() {
+ FileUseMap m = new FileUseMap(200, 0);
+
+ assertEquals(200, m.size());
+ assertEquals(0, m.usedSize());
+
+ m.add(10, 20, new Object());
+ assertEquals(200, m.size());
+ assertEquals(20, m.usedSize());
+ }
+}