blob: b6042156cdda22348adcc24a2aa3e8fa5c77a6b5 [file] [log] [blame]
/*
* Copyright (C) 2017 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.apk.analyzer.dex;
import static com.android.tools.apk.analyzer.dex.DexFiles.getDexFile;
import static org.junit.Assert.assertEquals;
import com.android.annotations.NonNull;
import com.android.testutils.TestResources;
import com.android.tools.apk.analyzer.FilteredTreeModel;
import com.android.tools.apk.analyzer.dex.tree.DexElementNode;
import com.android.tools.apk.analyzer.dex.tree.DexPackageNode;
import com.android.tools.proguard.ProguardMap;
import com.android.tools.proguard.ProguardSeedsMap;
import com.android.tools.proguard.ProguardUsagesMap;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.ParseException;
import javax.swing.tree.TreeModel;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.junit.Test;
public class FilteredTreeModelTest {
@Test
public void fieldsAndMethodReferenceTree() throws IOException {
DexBackedDexFile dexFile = getTestDexFile("Test2.dex");
DexPackageNode packageTreeNode =
new PackageTreeCreator(null, false).constructPackageTree(dexFile);
DexViewFilters options = new DexViewFilters();
options.setShowFields(true);
options.setShowMethods(true);
options.setShowReferencedNodes(true);
FilteredTreeModel filteredTreeModel = new FilteredTreeModel<>(packageTreeNode, options);
StringBuffer sb = new StringBuffer(100);
dumpTree(sb, filteredTreeModel, packageTreeNode, 0);
assertEquals(
"root: 6,11\n"
+ " java: 0,4\n"
+ " lang: 0,3\n"
+ " Integer: 0,1\n"
+ " java.lang.Integer valueOf(int): 0,1\n"
+ " Object: 0,1\n"
+ " <init>(): 0,1\n"
+ " Boolean: 0,1\n"
+ " java.lang.Boolean valueOf(boolean): 0,1\n"
+ " annotation: 0,0\n"
+ " RetentionPolicy: 0,0\n"
+ " java.lang.annotation.RetentionPolicy RUNTIME: 0,0\n"
+ " util: 0,1\n"
+ " Collections: 0,1\n"
+ " java.util.List emptyList(): 0,1\n"
+ " Test2: 3,3\n"
+ " <init>(): 1,1\n"
+ " java.lang.Integer get(): 1,1\n"
+ " java.util.List getList(): 1,1\n"
+ " a aClassField: 0,0\n"
+ " int aField: 0,0\n"
+ " TestSubclass: 2,3\n"
+ " <init>(): 1,1\n"
+ " java.util.List getAnotherList(): 1,1\n"
+ " java.util.List getList(): 0,1\n"
+ " a: 1,1\n"
+ " <init>(): 1,1\n"
+ " SomeAnnotation: 0,0\n",
sb.toString());
}
@Test
public void fieldsOnlyReferenceTree() throws IOException {
DexBackedDexFile dexFile = getTestDexFile("Test2.dex");
DexPackageNode packageTreeNode =
new PackageTreeCreator(null, false).constructPackageTree(dexFile);
DexViewFilters options = new DexViewFilters();
options.setShowFields(true);
options.setShowMethods(false);
options.setShowReferencedNodes(true);
FilteredTreeModel filteredTreeModel = new FilteredTreeModel<>(packageTreeNode, options);
StringBuffer sb = new StringBuffer(100);
dumpTree(sb, filteredTreeModel, packageTreeNode, 0);
assertEquals(
"root: 6,11\n"
+ " java: 0,4\n"
+ " lang: 0,3\n"
+ " Integer: 0,1\n"
+ " Object: 0,1\n"
+ " Boolean: 0,1\n"
+ " annotation: 0,0\n"
+ " RetentionPolicy: 0,0\n"
+ " java.lang.annotation.RetentionPolicy RUNTIME: 0,0\n"
+ " util: 0,1\n"
+ " Collections: 0,1\n"
+ " Test2: 3,3\n"
+ " a aClassField: 0,0\n"
+ " int aField: 0,0\n"
+ " TestSubclass: 2,3\n"
+ " a: 1,1\n"
+ " SomeAnnotation: 0,0\n",
sb.toString());
}
@Test
public void definedOnlyReferenceTree() throws IOException {
DexBackedDexFile dexFile = getTestDexFile("Test2.dex");
DexPackageNode packageTreeNode =
new PackageTreeCreator(null, false).constructPackageTree(dexFile);
DexViewFilters options = new DexViewFilters();
options.setShowFields(true);
options.setShowMethods(true);
options.setShowReferencedNodes(false);
FilteredTreeModel filteredTreeModel = new FilteredTreeModel<>(packageTreeNode, options);
StringBuffer sb = new StringBuffer(100);
dumpTree(sb, filteredTreeModel, packageTreeNode, 0);
assertEquals(
"root: 6,11\n"
+ " Test2: 3,3\n"
+ " <init>(): 1,1\n"
+ " java.lang.Integer get(): 1,1\n"
+ " java.util.List getList(): 1,1\n"
+ " a aClassField: 0,0\n"
+ " int aField: 0,0\n"
+ " TestSubclass: 2,3\n"
+ " <init>(): 1,1\n"
+ " java.util.List getAnotherList(): 1,1\n"
+ " a: 1,1\n"
+ " <init>(): 1,1\n"
+ " SomeAnnotation: 0,0\n",
sb.toString());
}
@Test
public void methodsOnlyReferenceTree() throws IOException {
DexBackedDexFile dexFile = getTestDexFile("Test2.dex");
DexPackageNode packageTreeNode =
new PackageTreeCreator(null, false).constructPackageTree(dexFile);
DexViewFilters options = new DexViewFilters();
options.setShowFields(false);
options.setShowMethods(true);
options.setShowReferencedNodes(true);
FilteredTreeModel filteredTreeModel = new FilteredTreeModel<>(packageTreeNode, options);
StringBuffer sb = new StringBuffer(100);
dumpTree(sb, filteredTreeModel, packageTreeNode, 0);
assertEquals(
"root: 6,11\n"
+ " java: 0,4\n"
+ " lang: 0,3\n"
+ " Integer: 0,1\n"
+ " java.lang.Integer valueOf(int): 0,1\n"
+ " Object: 0,1\n"
+ " <init>(): 0,1\n"
+ " Boolean: 0,1\n"
+ " java.lang.Boolean valueOf(boolean): 0,1\n"
+ " annotation: 0,0\n"
+ " RetentionPolicy: 0,0\n"
+ " util: 0,1\n"
+ " Collections: 0,1\n"
+ " java.util.List emptyList(): 0,1\n"
+ " Test2: 3,3\n"
+ " <init>(): 1,1\n"
+ " java.lang.Integer get(): 1,1\n"
+ " java.util.List getList(): 1,1\n"
+ " TestSubclass: 2,3\n"
+ " <init>(): 1,1\n"
+ " java.util.List getAnotherList(): 1,1\n"
+ " java.util.List getList(): 0,1\n"
+ " a: 1,1\n"
+ " <init>(): 1,1\n"
+ " SomeAnnotation: 0,0\n",
sb.toString());
}
@Test
public void removedNodesReferenceTree() throws IOException, ParseException {
DexBackedDexFile dexFile = getTestDexFile("Test2.dex");
Path mapPath = TestResources.getFile("/Test2_mapping.txt").toPath();
ProguardMap map = new ProguardMap();
map.readFromReader(Files.newBufferedReader(mapPath));
mapPath = TestResources.getFile("/Test2_seeds.txt").toPath();
ProguardSeedsMap seedsMap = ProguardSeedsMap.parse(Files.newBufferedReader(mapPath));
mapPath = TestResources.getFile("/Test2_usage.txt").toPath();
ProguardUsagesMap usageMap = ProguardUsagesMap.parse(Files.newBufferedReader(mapPath));
ProguardMappings proguardMappings = new ProguardMappings(map, seedsMap, usageMap);
DexPackageNode packageTreeNode =
new PackageTreeCreator(proguardMappings, true).constructPackageTree(dexFile);
DexViewFilters options = new DexViewFilters();
options.setShowFields(true);
options.setShowMethods(true);
options.setShowReferencedNodes(true);
options.setShowRemovedNodes(false);
FilteredTreeModel filteredTreeModel = new FilteredTreeModel(packageTreeNode, options);
StringBuffer sb = new StringBuffer(100);
dumpTree(sb, filteredTreeModel, packageTreeNode, 0);
assertEquals(
"root: 6,11\n"
+ " java: 0,4\n"
+ " lang: 0,3\n"
+ " Integer: 0,1\n"
+ " java.lang.Integer valueOf(int): 0,1\n"
+ " Object: 0,1\n"
+ " <init>(): 0,1\n"
+ " Boolean: 0,1\n"
+ " java.lang.Boolean valueOf(boolean): 0,1\n"
+ " annotation: 0,0\n"
+ " RetentionPolicy: 0,0\n"
+ " java.lang.annotation.RetentionPolicy RUNTIME: 0,0\n"
+ " util: 0,1\n"
+ " Collections: 0,1\n"
+ " java.util.List emptyList(): 0,1\n"
+ " Test2: 3,3\n"
+ " <init>(): 1,1\n"
+ " java.lang.Integer get(): 1,1\n"
+ " java.util.List getList(): 1,1\n"
+ " AnotherClass aClassField: 0,0\n"
+ " int aField: 0,0\n"
+ " TestSubclass: 2,3\n"
+ " <init>(): 1,1\n"
+ " java.util.List getAnotherList(): 1,1\n"
+ " java.util.List getList(): 0,1\n"
+ " AnotherClass: 1,1\n"
+ " <init>(): 1,1\n"
+ " SomeAnnotation: 0,0\n",
sb.toString());
options.setShowRemovedNodes(true);
sb.setLength(0);
dumpTree(sb, filteredTreeModel, packageTreeNode, 0);
assertEquals(
"root: 6,11\n"
+ " java: 0,4\n"
+ " lang: 0,3\n"
+ " Integer: 0,1\n"
+ " java.lang.Integer valueOf(int): 0,1\n"
+ " Object: 0,1\n"
+ " <init>(): 0,1\n"
+ " Boolean: 0,1\n"
+ " java.lang.Boolean valueOf(boolean): 0,1\n"
+ " annotation: 0,0\n"
+ " RetentionPolicy: 0,0\n"
+ " java.lang.annotation.RetentionPolicy RUNTIME: 0,0\n"
+ " util: 0,1\n"
+ " Collections: 0,1\n"
+ " java.util.List emptyList(): 0,1\n"
+ " Test2: 3,3\n"
+ " <init>(): 1,1\n"
+ " java.lang.Integer get(): 1,1\n"
+ " java.util.List getList(): 1,1\n"
+ " AnotherClass aClassField: 0,0\n"
+ " int aField: 0,0\n"
+ " TestSubclass: 2,3\n"
+ " <init>(): 1,1\n"
+ " java.util.List getAnotherList(): 1,1\n"
+ " java.util.List getList(): 0,1\n"
+ " AnotherClass: 1,1\n"
+ " <init>(): 1,1\n"
+ " AnotherClass(int,TestSubclass): 0,0\n"
+ " SomeAnnotation: 0,0\n"
+ " RemovedSubclass: 0,0\n",
sb.toString());
}
@NonNull
private static DexBackedDexFile getTestDexFile(String filename) throws IOException {
Path dexPath = TestResources.getFile("/" + filename).toPath();
return getDexFile(Files.readAllBytes(dexPath));
}
private static void dumpTree(
StringBuffer sb, @NonNull TreeModel model, DexElementNode node, int depth) {
for (int i = 0; i < depth * 2; i++) {
sb.append(' ');
}
sb.append(node.getName());
sb.append(": ");
sb.append(node.getMethodDefinitionsCount());
sb.append(',');
sb.append(node.getMethodReferencesCount());
sb.append('\n');
int count = model.getChildCount(node);
for (int i = 0; i < count; i++) {
dumpTree(sb, model, (DexElementNode) model.getChild(node, i), depth + 1);
}
}
}