1 /* 2 * Copyright 2016, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.dexlib2; 33 34 import com.beust.jcommander.internal.Maps; 35 import com.google.common.collect.Lists; 36 import org.jf.dexlib2.DexFileFactory.DexEntryFinder; 37 import org.jf.dexlib2.DexFileFactory.DexFileNotFoundException; 38 import org.jf.dexlib2.DexFileFactory.MultipleMatchingDexEntriesException; 39 import org.jf.dexlib2.DexFileFactory.UnsupportedFileTypeException; 40 import org.jf.dexlib2.dexbacked.DexBackedDexFile; 41 import org.jf.dexlib2.dexbacked.DexBackedDexFile.NotADexFile; 42 import org.jf.dexlib2.iface.MultiDexContainer; 43 import org.junit.Assert; 44 import org.junit.Test; 45 46 import javax.annotation.Nonnull; 47 import javax.annotation.Nullable; 48 import java.io.IOException; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.Map.Entry; 52 53 import static org.mockito.Mockito.mock; 54 55 public class DexEntryFinderTest { 56 57 @Test testNormalStuff()58 public void testNormalStuff() throws Exception { 59 Map<String, DexBackedDexFile> entries = Maps.newHashMap(); 60 DexBackedDexFile dexFile1 = mock(DexBackedDexFile.class); 61 entries.put("/system/framework/framework.jar", dexFile1); 62 DexBackedDexFile dexFile2 = mock(DexBackedDexFile.class); 63 entries.put("/system/framework/framework.jar:classes2.dex", dexFile2); 64 DexEntryFinder testFinder = new DexEntryFinder("blah.oat", new TestMultiDexContainer(entries)); 65 66 Assert.assertEquals(dexFile1, testFinder.findEntry("/system/framework/framework.jar", true)); 67 68 assertEntryNotFound(testFinder, "system/framework/framework.jar", true); 69 assertEntryNotFound(testFinder, "/framework/framework.jar", true); 70 assertEntryNotFound(testFinder, "framework/framework.jar", true); 71 assertEntryNotFound(testFinder, "/framework.jar", true); 72 assertEntryNotFound(testFinder, "framework.jar", true); 73 74 Assert.assertEquals(dexFile1, testFinder.findEntry("system/framework/framework.jar", false)); 75 Assert.assertEquals(dexFile1, testFinder.findEntry("/framework/framework.jar", false)); 76 Assert.assertEquals(dexFile1, testFinder.findEntry("framework/framework.jar", false)); 77 Assert.assertEquals(dexFile1, testFinder.findEntry("/framework.jar", false)); 78 Assert.assertEquals(dexFile1, testFinder.findEntry("framework.jar", false)); 79 80 assertEntryNotFound(testFinder, "ystem/framework/framework.jar", false); 81 assertEntryNotFound(testFinder, "ssystem/framework/framework.jar", false); 82 assertEntryNotFound(testFinder, "ramework/framework.jar", false); 83 assertEntryNotFound(testFinder, "ramework.jar", false); 84 assertEntryNotFound(testFinder, "framework", false); 85 86 Assert.assertEquals(dexFile2, testFinder.findEntry("/system/framework/framework.jar:classes2.dex", true)); 87 88 assertEntryNotFound(testFinder, "system/framework/framework.jar:classes2.dex", true); 89 assertEntryNotFound(testFinder, "framework.jar:classes2.dex", true); 90 assertEntryNotFound(testFinder, "classes2.dex", true); 91 92 Assert.assertEquals(dexFile2, testFinder.findEntry("system/framework/framework.jar:classes2.dex", false)); 93 Assert.assertEquals(dexFile2, testFinder.findEntry("/framework/framework.jar:classes2.dex", false)); 94 Assert.assertEquals(dexFile2, testFinder.findEntry("framework/framework.jar:classes2.dex", false)); 95 Assert.assertEquals(dexFile2, testFinder.findEntry("/framework.jar:classes2.dex", false)); 96 Assert.assertEquals(dexFile2, testFinder.findEntry("framework.jar:classes2.dex", false)); 97 Assert.assertEquals(dexFile2, testFinder.findEntry(":classes2.dex", false)); 98 Assert.assertEquals(dexFile2, testFinder.findEntry("classes2.dex", false)); 99 100 assertEntryNotFound(testFinder, "ystem/framework/framework.jar:classes2.dex", false); 101 assertEntryNotFound(testFinder, "ramework.jar:classes2.dex", false); 102 assertEntryNotFound(testFinder, "lasses2.dex", false); 103 assertEntryNotFound(testFinder, "classes2", false); 104 } 105 106 @Test testSimilarEntries()107 public void testSimilarEntries() throws Exception { 108 Map<String, DexBackedDexFile> entries = Maps.newHashMap(); 109 DexBackedDexFile dexFile1 = mock(DexBackedDexFile.class); 110 entries.put("/system/framework/framework.jar", dexFile1); 111 DexBackedDexFile dexFile2 = mock(DexBackedDexFile.class); 112 entries.put("system/framework/framework.jar", dexFile2); 113 DexEntryFinder testFinder = new DexEntryFinder("blah.oat", new TestMultiDexContainer(entries)); 114 115 Assert.assertEquals(dexFile1, testFinder.findEntry("/system/framework/framework.jar", true)); 116 Assert.assertEquals(dexFile2, testFinder.findEntry("system/framework/framework.jar", true)); 117 118 assertMultipleMatchingEntries(testFinder, "/system/framework/framework.jar"); 119 assertMultipleMatchingEntries(testFinder, "system/framework/framework.jar"); 120 121 assertMultipleMatchingEntries(testFinder, "/framework/framework.jar"); 122 assertMultipleMatchingEntries(testFinder, "framework/framework.jar"); 123 assertMultipleMatchingEntries(testFinder, "/framework.jar"); 124 assertMultipleMatchingEntries(testFinder, "framework.jar"); 125 } 126 127 @Test testMatchingSuffix()128 public void testMatchingSuffix() throws Exception { 129 Map<String, DexBackedDexFile> entries = Maps.newHashMap(); 130 DexBackedDexFile dexFile1 = mock(DexBackedDexFile.class); 131 entries.put("/system/framework/framework.jar", dexFile1); 132 DexBackedDexFile dexFile2 = mock(DexBackedDexFile.class); 133 entries.put("/framework/framework.jar", dexFile2); 134 DexEntryFinder testFinder = new DexEntryFinder("blah.oat", new TestMultiDexContainer(entries)); 135 136 Assert.assertEquals(dexFile1, testFinder.findEntry("/system/framework/framework.jar", true)); 137 Assert.assertEquals(dexFile2, testFinder.findEntry("/framework/framework.jar", true)); 138 139 Assert.assertEquals(dexFile2, testFinder.findEntry("/framework/framework.jar", false)); 140 Assert.assertEquals(dexFile2, testFinder.findEntry("framework/framework.jar", false)); 141 142 assertMultipleMatchingEntries(testFinder, "/framework.jar"); 143 assertMultipleMatchingEntries(testFinder, "framework.jar"); 144 } 145 146 @Test testNonDexEntries()147 public void testNonDexEntries() throws Exception { 148 Map<String, DexBackedDexFile> entries = Maps.newHashMap(); 149 DexBackedDexFile dexFile1 = mock(DexBackedDexFile.class); 150 entries.put("classes.dex", dexFile1); 151 entries.put("/blah/classes.dex", null); 152 DexEntryFinder testFinder = new DexEntryFinder("blah.oat", new TestMultiDexContainer(entries)); 153 154 Assert.assertEquals(dexFile1, testFinder.findEntry("classes.dex", true)); 155 Assert.assertEquals(dexFile1, testFinder.findEntry("classes.dex", false)); 156 157 assertUnsupportedFileType(testFinder, "/blah/classes.dex", true); 158 assertDexFileNotFound(testFinder, "/blah/classes.dex", false); 159 } 160 assertEntryNotFound(DexEntryFinder finder, String entry, boolean exactMatch)161 private void assertEntryNotFound(DexEntryFinder finder, String entry, boolean exactMatch) throws IOException { 162 try { 163 finder.findEntry(entry, exactMatch); 164 Assert.fail(); 165 } catch (DexFileNotFoundException ex) { 166 // expected exception 167 } 168 } 169 assertMultipleMatchingEntries(DexEntryFinder finder, String entry)170 private void assertMultipleMatchingEntries(DexEntryFinder finder, String entry) throws IOException { 171 try { 172 finder.findEntry(entry, false); 173 Assert.fail(); 174 } catch (MultipleMatchingDexEntriesException ex) { 175 // expected exception 176 } 177 } 178 assertUnsupportedFileType(DexEntryFinder finder, String entry, boolean exactMatch)179 private void assertUnsupportedFileType(DexEntryFinder finder, String entry, boolean exactMatch) throws IOException { 180 try { 181 finder.findEntry(entry, exactMatch); 182 Assert.fail(); 183 } catch (UnsupportedFileTypeException ex) { 184 // expected exception 185 } 186 } 187 assertDexFileNotFound(DexEntryFinder finder, String entry, boolean exactMatch)188 private void assertDexFileNotFound(DexEntryFinder finder, String entry, boolean exactMatch) throws IOException { 189 try { 190 finder.findEntry(entry, exactMatch); 191 Assert.fail(); 192 } catch (DexFileNotFoundException ex) { 193 // expected exception 194 } 195 } 196 197 public static class TestMultiDexContainer implements MultiDexContainer<DexBackedDexFile> { 198 @Nonnull private final Map<String, DexBackedDexFile> entries; 199 TestMultiDexContainer(@onnull Map<String, DexBackedDexFile> entries)200 public TestMultiDexContainer(@Nonnull Map<String, DexBackedDexFile> entries) { 201 this.entries = entries; 202 } 203 getDexEntryNames()204 @Nonnull @Override public List<String> getDexEntryNames() throws IOException { 205 List<String> entryNames = Lists.newArrayList(); 206 207 for (Entry<String, DexBackedDexFile> entry: entries.entrySet()) { 208 if (entry.getValue() != null) { 209 entryNames.add(entry.getKey()); 210 } 211 } 212 213 return entryNames; 214 } 215 getEntry(@onnull String entryName)216 @Nullable @Override public DexBackedDexFile getEntry(@Nonnull String entryName) throws IOException { 217 if (entries.containsKey(entryName)) { 218 DexBackedDexFile entry = entries.get(entryName); 219 if (entry == null) { 220 throw new NotADexFile(); 221 } 222 return entry; 223 } 224 return null; 225 } 226 getOpcodes()227 @Nonnull @Override public Opcodes getOpcodes() { 228 return Opcodes.getDefault(); 229 } 230 } 231 } 232