Home | History | Annotate | Download | only in dexlib2
      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
     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
    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
    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
    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 
    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 
    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 
    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 
    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 
    200         public TestMultiDexContainer(@Nonnull Map<String, DexBackedDexFile> entries) {
    201             this.entries = entries;
    202         }
    203 
    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 
    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 
    227         @Nonnull @Override public Opcodes getOpcodes() {
    228             return Opcodes.getDefault();
    229         }
    230     }
    231 }
    232