aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google/devtools/build/android/dexer
diff options
context:
space:
mode:
authorGravatar kmb <kmb@google.com>2017-10-25 23:40:30 +0200
committerGravatar Dmitry Lomov <dslomov@google.com>2017-10-26 10:59:31 +0200
commitb47a62ed03a529319ec79806672ee57ea6819b30 (patch)
tree0494832fb0844b2ed64d939cb20d32e35cd91776 /src/test/java/com/google/devtools/build/android/dexer
parent0356a506ceb02da557c0acbc7c50b598bc37620e (diff)
Internal change
PiperOrigin-RevId: 173451767
Diffstat (limited to 'src/test/java/com/google/devtools/build/android/dexer')
-rw-r--r--src/test/java/com/google/devtools/build/android/dexer/BUILD2
-rw-r--r--src/test/java/com/google/devtools/build/android/dexer/DexFileMergerTest.java30
-rw-r--r--src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java245
-rw-r--r--src/test/java/com/google/devtools/build/android/dexer/DexLimitTrackerTest.java96
4 files changed, 358 insertions, 15 deletions
diff --git a/src/test/java/com/google/devtools/build/android/dexer/BUILD b/src/test/java/com/google/devtools/build/android/dexer/BUILD
index 2f70378fee..c60e7474d7 100644
--- a/src/test/java/com/google/devtools/build/android/dexer/BUILD
+++ b/src/test/java/com/google/devtools/build/android/dexer/BUILD
@@ -22,6 +22,8 @@ java_library(
exclude = [
"NoAndroidSdkStubTest.java",
"AllTests.java",
+ # TODO(kmb,ajmichael): need 2nd Java 7 Jar for this test
+ "DexFileSplitterTest.java",
],
),
"//conditions:default": ["NoAndroidSdkStubTest.java"],
diff --git a/src/test/java/com/google/devtools/build/android/dexer/DexFileMergerTest.java b/src/test/java/com/google/devtools/build/android/dexer/DexFileMergerTest.java
index d4fb9a33f2..c772e427e8 100644
--- a/src/test/java/com/google/devtools/build/android/dexer/DexFileMergerTest.java
+++ b/src/test/java/com/google/devtools/build/android/dexer/DexFileMergerTest.java
@@ -1,16 +1,16 @@
// Copyright 2017 The Bazel Authors. 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.
+//
+// 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.devtools.build.android.dexer;
import static com.google.common.truth.Truth.assertThat;
@@ -91,7 +91,7 @@ public class DexFileMergerTest {
@Test
public void testMergeDexArchive_multidex() throws Exception {
Path dexArchive = buildDexArchive();
- Path outputArchive = runDexFileMerger(dexArchive, 20, "multidex_from_dex_archive.dex.zip");
+ Path outputArchive = runDexFileMerger(dexArchive, 200, "multidex_from_dex_archive.dex.zip");
int expectedClassCount = matchingFileCount(dexArchive, ".*\\.class.dex$");
assertMultidexOutput(expectedClassCount, outputArchive, ImmutableSet.<String>of());
@@ -228,7 +228,7 @@ public class DexFileMergerTest {
Set<String> shard = dexFiles.get(expectedDexFileName(i));
for (String c1 : prev) {
for (String c2 : shard) {
- assertThat(DexFileMerger.compareClassNames(c2, c1))
+ assertThat(ZipEntryComparator.compareClassNames(c2, c1))
.named(c2 + " in shard " + i + " should compare as larger than " + c1
+ "; list of all shards for reference: " + dexFiles)
.isGreaterThan(0);
@@ -251,7 +251,7 @@ public class DexFileMergerTest {
}
Multimap<String, String> dexFiles =
assertMultidexOutput(expectedClassCount, outputArchive, mainDexList);
- assertThat(dexFiles.keySet()).hasSize(2);
+ assertThat(dexFiles.keySet().size()).isAtLeast(2);
if (minimalMainDex) {
assertThat(dexFiles.get("classes.dex")).containsExactlyElementsIn(mainDexList);
} else {
diff --git a/src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java b/src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java
new file mode 100644
index 0000000000..c2354ca134
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java
@@ -0,0 +1,245 @@
+// Copyright 2017 The Bazel Authors. 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.devtools.build.android.dexer;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.android.dx.command.dexer.DxContext;
+import com.android.dx.dex.code.PositionList;
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link DexFileSplitter}. */
+@RunWith(JUnit4.class)
+public class DexFileSplitterTest {
+
+ private static final Path WORKING_DIR = Paths.get(System.getProperty("user.dir"));
+ private static final Path INPUT_JAR = WORKING_DIR.resolve(System.getProperty("testinputjar"));
+ private static final Path INPUT_JAR2 = WORKING_DIR.resolve(System.getProperty("testinputjar2"));
+ private static final Path MAIN_DEX_LIST_FILE =
+ WORKING_DIR.resolve(System.getProperty("testmaindexlist"));
+ static final String DEX_PREFIX = "classes";
+
+ @Test
+ public void testSingleInputSingleOutput() throws Exception {
+ Path dexArchive = buildDexArchive();
+ ImmutableList<Path> outputArchives = runDexSplitter(256 * 256, "from_single", dexArchive);
+ assertThat(outputArchives).hasSize(1);
+
+ ImmutableSet<String> expectedFiles = dexEntries(dexArchive);
+ assertThat(dexEntries(outputArchives.get(0))).containsExactlyElementsIn(expectedFiles);
+ }
+
+ @Test
+ public void testDuplicateInputIgnored() throws Exception {
+ Path dexArchive = buildDexArchive();
+ ImmutableList<Path> outputArchives =
+ runDexSplitter(256 * 256, "from_duplicate", dexArchive, dexArchive);
+ assertThat(outputArchives).hasSize(1);
+
+ ImmutableSet<String> expectedFiles = dexEntries(dexArchive);
+ assertThat(dexEntries(outputArchives.get(0))).containsExactlyElementsIn(expectedFiles);
+ }
+
+ @Test
+ public void testSingleInputMultidexOutput() throws Exception {
+ Path dexArchive = buildDexArchive();
+ ImmutableList<Path> outputArchives = runDexSplitter(200, "multidex_from_single", dexArchive);
+ assertThat(outputArchives.size()).isGreaterThan(1); // test sanity
+
+ ImmutableSet<String> expectedEntries = dexEntries(dexArchive);
+ assertExpectedEntries(outputArchives, expectedEntries);
+ }
+
+ @Test
+ public void testMultipleInputsMultidexOutput() throws Exception {
+ Path dexArchive = buildDexArchive();
+ Path dexArchive2 = buildDexArchive(INPUT_JAR2, "jar2.dex.zip");
+ ImmutableList<Path> outputArchives = runDexSplitter(200, "multidex", dexArchive, dexArchive2);
+ assertThat(outputArchives.size()).isGreaterThan(1); // test sanity
+
+ HashSet<String> expectedEntries = new HashSet<>();
+ expectedEntries.addAll(dexEntries(dexArchive));
+ expectedEntries.addAll(dexEntries(dexArchive2));
+ assertExpectedEntries(outputArchives, expectedEntries);
+ }
+
+ @Test
+ public void testMainDexList() throws Exception {
+ Path dexArchive = buildDexArchive();
+ ImmutableList<Path> outputArchives =
+ runDexSplitter(
+ 200,
+ "main_dex_list",
+ MAIN_DEX_LIST_FILE,
+ /*minimalMainDex=*/ false,
+ dexArchive);
+
+ ImmutableSet<String> expectedEntries = dexEntries(dexArchive);
+ assertThat(outputArchives.size()).isGreaterThan(1); // test sanity
+ assertThat(dexEntries(outputArchives.get(0)))
+ .containsAllIn(expectedMainDexEntries());
+ assertExpectedEntries(outputArchives, expectedEntries);
+ }
+
+ @Test
+ public void testMinimalMainDex() throws Exception {
+ Path dexArchive = buildDexArchive();
+ ImmutableList<Path> outputArchives =
+ runDexSplitter(
+ 256 * 256,
+ "minimal_main_dex",
+ MAIN_DEX_LIST_FILE,
+ /*minimalMainDex=*/ true,
+ dexArchive);
+
+ ImmutableSet<String> expectedEntries = dexEntries(dexArchive);
+ assertThat(outputArchives.size()).isGreaterThan(1); // test sanity
+ assertThat(dexEntries(outputArchives.get(0)))
+ .containsExactlyElementsIn(expectedMainDexEntries());
+ assertExpectedEntries(outputArchives, expectedEntries);
+ }
+
+ private static Iterable<String> expectedMainDexEntries() throws IOException {
+ return Iterables.transform(
+ Files.readAllLines(MAIN_DEX_LIST_FILE),
+ new Function<String, String>() {
+ @Override
+ public String apply(String input) {
+ return input + ".dex";
+ }
+ });
+ }
+
+ @Test
+ public void testMultidexOffWithMultidexFlags() throws Exception {
+ Path dexArchive = buildDexArchive();
+ try {
+ runDexSplitter(
+ 200,
+ "should_fail",
+ /*mainDexList=*/ null,
+ /*minimalMainDex=*/ true,
+ dexArchive);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo("--minimal-main-dex not allowed without --main-dex-list");
+ }
+ }
+
+ private void assertExpectedEntries(
+ ImmutableList<Path> outputArchives, Set<String> expectedEntries) throws IOException {
+ ImmutableSet.Builder<String> actualFiles = ImmutableSet.builder();
+ for (Path outputArchive : outputArchives) {
+ actualFiles.addAll(dexEntries(outputArchive));
+ }
+ // ImmutableSet.Builder.build would fail if there were duplicates. Additionally we make sure
+ // all expected files are here
+ assertThat(actualFiles.build()).containsExactlyElementsIn(expectedEntries);
+ }
+
+ private ImmutableSet<String> dexEntries(Path dexArchive) throws IOException {
+ try (ZipFile input = new ZipFile(dexArchive.toFile())) {
+ ImmutableSet<String> result = ImmutableSet.copyOf(Iterators.filter(
+ Iterators.transform(Iterators.forEnumeration(input.entries()), ZipEntryName.INSTANCE),
+ Predicates.containsPattern(".*\\.class.dex$")));
+ assertThat(result).isNotEmpty(); // test sanity
+ return result;
+ }
+ }
+
+ private ImmutableList<Path> runDexSplitter(int maxNumberOfIdxPerDex, String outputRoot,
+ Path... dexArchives) throws IOException {
+ return runDexSplitter(
+ maxNumberOfIdxPerDex,
+ outputRoot,
+ /*mainDexList=*/ null,
+ /*minimalMainDex=*/ false,
+ dexArchives);
+ }
+
+ private ImmutableList<Path> runDexSplitter(
+ int maxNumberOfIdxPerDex,
+ String outputRoot,
+ @Nullable Path mainDexList,
+ boolean minimalMainDex,
+ Path... dexArchives)
+ throws IOException {
+ DexFileSplitter.Options options = new DexFileSplitter.Options();
+ options.inputArchives = ImmutableList.copyOf(dexArchives);
+ options.outputDirectory =
+ FileSystems.getDefault().getPath(System.getenv("TEST_TMPDIR"), outputRoot);
+ options.maxNumberOfIdxPerDex = maxNumberOfIdxPerDex;
+ options.mainDexListFile = mainDexList;
+ options.minimalMainDex = minimalMainDex;
+ DexFileSplitter.splitIntoShards(options);
+ assertThat(options.outputDirectory.toFile().exists()).isTrue();
+ ImmutableSet<Path> files =
+ ImmutableSet.copyOf(Files.newDirectoryStream(options.outputDirectory, "*.zip"));
+ ImmutableList.Builder<Path> result = ImmutableList.builder();
+ for (int i = 1; i <= files.size(); ++i) {
+ Path path = options.outputDirectory.resolve(i + ".shard.zip");
+ assertThat(files).contains(path);
+ result.add(path);
+ }
+ return result.build(); // return expected files in sorted order
+ }
+
+ private Path buildDexArchive() throws Exception {
+ return buildDexArchive(INPUT_JAR, "libtests.dex.zip");
+ }
+
+ private Path buildDexArchive(Path inputJar, String outputZip) throws Exception {
+ DexBuilder.Options options = new DexBuilder.Options();
+ // Use Jar file that has this test in it as the input Jar
+ options.inputJar = inputJar;
+ options.outputZip =
+ FileSystems.getDefault().getPath(System.getenv("TEST_TMPDIR"), outputZip);
+ options.maxThreads = 1;
+ Dexing.DexingOptions dexingOptions = new Dexing.DexingOptions();
+ dexingOptions.optimize = true;
+ dexingOptions.positionInfo = PositionList.LINES;
+ DexBuilder.buildDexArchive(options, new Dexing(new DxContext(), dexingOptions));
+ return options.outputZip;
+ }
+
+ // Can't use lambda for Java 7 compatibility so we can run this Jar through dx without desugaring.
+ private enum ZipEntryName implements Function<ZipEntry, String> {
+ INSTANCE;
+ @Override
+ public String apply(ZipEntry input) {
+ return input.getName();
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/android/dexer/DexLimitTrackerTest.java b/src/test/java/com/google/devtools/build/android/dexer/DexLimitTrackerTest.java
new file mode 100644
index 0000000000..a1e6668ac9
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/dexer/DexLimitTrackerTest.java
@@ -0,0 +1,96 @@
+// Copyright 2017 The Bazel Authors. 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.devtools.build.android.dexer;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.dex.Dex;
+import com.android.dx.command.dexer.DxContext;
+import com.android.dx.dex.DexOptions;
+import com.android.dx.dex.cf.CfOptions;
+import com.android.dx.dex.file.DexFile;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link DexLimitTracker}. */
+@RunWith(JUnit4.class)
+public class DexLimitTrackerTest {
+
+ private Dex dex;
+
+ @Before
+ public void setUp() throws IOException {
+ dex = DexFiles.toDex(convertClass(DexLimitTrackerTest.class));
+ }
+
+ @Test
+ public void testUnderLimit() {
+ DexLimitTracker tracker =
+ new DexLimitTracker(Math.max(dex.methodIds().size(), dex.fieldIds().size()));
+ assertThat(tracker.track(dex)).isFalse();
+ }
+
+ @Test
+ public void testOverLimit() throws IOException {
+ DexLimitTracker tracker =
+ new DexLimitTracker(Math.max(dex.methodIds().size(), dex.fieldIds().size()) - 1);
+ assertThat(tracker.track(dex)).isTrue();
+ assertThat(tracker.track(dex)).isTrue();
+ assertThat(tracker.track(DexFiles.toDex(convertClass(DexLimitTracker.class)))).isTrue();
+ }
+
+ @Test
+ public void testRepeatedReferencesDeduped() throws IOException {
+ DexLimitTracker tracker =
+ new DexLimitTracker(Math.max(dex.methodIds().size(), dex.fieldIds().size()));
+ assertThat(tracker.track(dex)).isFalse();
+ assertThat(tracker.track(dex)).isFalse();
+ assertThat(tracker.track(dex)).isFalse();
+ assertThat(tracker.track(dex)).isFalse();
+ assertThat(tracker.track(DexFiles.toDex(convertClass(DexLimitTracker.class)))).isTrue();
+ assertThat(tracker.track(dex)).isTrue();
+ }
+
+ @Test
+ public void testGoOverLimit() throws IOException {
+ DexLimitTracker tracker =
+ new DexLimitTracker(Math.max(dex.methodIds().size(), dex.fieldIds().size()));
+ assertThat(tracker.track(dex)).isFalse();
+ assertThat(tracker.track(DexFiles.toDex(convertClass(DexLimitTracker.class)))).isTrue();
+ }
+
+ @Test
+ public void testClear() throws IOException {
+ DexLimitTracker tracker =
+ new DexLimitTracker(Math.max(dex.methodIds().size(), dex.fieldIds().size()));
+ assertThat(tracker.track(dex)).isFalse();
+ assertThat(tracker.track(DexFiles.toDex(convertClass(DexLimitTracker.class)))).isTrue();
+ tracker.clear();
+ assertThat(tracker.track(dex)).isFalse();
+ }
+
+ private static DexFile convertClass(Class<?> clazz) throws IOException {
+ String path = clazz.getName().replace('.', '/') + ".class";
+ try (InputStream in =
+ Thread.currentThread().getContextClassLoader().getResourceAsStream(path)) {
+ return new DexConverter(new Dexing(new DxContext(), new DexOptions(), new CfOptions()))
+ .toDexFile(ByteStreams.toByteArray(in), path);
+ }
+ }
+}