1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 package com.android.tools.layoutlib.create; 19 20 import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor; 21 import com.android.tools.layoutlib.create.AsmAnalyzer.Result; 22 import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer; 23 24 import org.junit.Test; 25 import org.objectweb.asm.ClassReader; 26 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.net.URL; 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.TreeMap; 35 36 import static org.junit.Assert.assertArrayEquals; 37 import static org.junit.Assert.assertEquals; 38 import static org.junit.Assert.assertNotNull; 39 40 /** 41 * Unit tests for some methods of {@link AsmAnalyzer}. 42 */ 43 public class AsmAnalyzerTest { 44 private static final List<String> MOCK_ANDROID_JAR; 45 private static final String[] DEFAULT_EXCLUDES = new String[]{"notjava.lang.JavaClass"}; 46 private static final String[] DEFAULT_INCLUDE_FILES = new String[]{"mock_android/data/data*"}; 47 48 static { 49 List<String> mockJar = new ArrayList<>(); 50 URL url = AsmAnalyzerTest.class.getClassLoader().getResource("data/mock_android.jar"); 51 assert url != null : "Unable to locate mock_android.jar"; url.getFile()52 mockJar.add(url.getFile()); 53 MOCK_ANDROID_JAR = Collections.unmodifiableList(mockJar); 54 } 55 getDefaultAnalyzer()56 private static AsmAnalyzer getDefaultAnalyzer() { 57 MockLog log = new MockLog(); 58 return new AsmAnalyzer(log, MOCK_ANDROID_JAR, null , 59 null /* includeGlobs */, DEFAULT_EXCLUDES, DEFAULT_INCLUDE_FILES, 60 new MethodReplacer[] {}); 61 } 62 63 @Test testParseZip()64 public void testParseZip() throws IOException { 65 Map<String, ClassReader> map = new TreeMap<>(); 66 Map<String, InputStream> filesFound = new TreeMap<>(); 67 68 getDefaultAnalyzer().parseZip(MOCK_ANDROID_JAR, map, filesFound); 69 70 assertArrayEquals(new String[] { 71 "mock_android.fake.FakeClass", 72 "mock_android.fake.InnerTest", 73 "mock_android.fake.InnerTest$1", 74 "mock_android.fake.InnerTest$DerivingClass", 75 "mock_android.fake.InnerTest$MyGenerics1", 76 "mock_android.fake.InnerTest$MyIntEnum", 77 "mock_android.fake.InnerTest$MyStaticInnerClass", 78 "mock_android.fake.InnerTest$NotStaticInner1", 79 "mock_android.fake.InnerTest$NotStaticInner2", 80 "mock_android.fake.subpackage.SubpackageClassA", 81 "mock_android.fake.subpackage.SubpackageClassB", 82 "mock_android.fake.subpackage.SubpackageClassC", 83 "mock_android.fake.subpackage.SubpackageClassC$InnerClass", 84 "mock_android.fake.subpackage.SubpackageClassC$StaticInnerClass", 85 "mock_android.fake2.FakeClass", 86 "mock_android.fake2.keep.DoNotRemove", 87 "mock_android.util.EmptyArray", 88 "mock_android.util.NotNeeded", 89 "mock_android.view.LibLoader", 90 "mock_android.view.View", 91 "mock_android.view.ViewGroup", 92 "mock_android.view.ViewGroup$LayoutParams", 93 "mock_android.view.ViewGroup$MarginLayoutParams", 94 "mock_android.widget.LinearLayout", 95 "mock_android.widget.LinearLayout$LayoutParams", 96 "mock_android.widget.TableLayout", 97 "mock_android.widget.TableLayout$LayoutParams", 98 "notjava.lang.JavaClass", 99 }, 100 map.keySet().toArray()); 101 assertArrayEquals(new String[] {"mock_android/data/dataFile"}, 102 filesFound.keySet().toArray()); 103 } 104 105 @Test testFindClass()106 public void testFindClass() throws IOException { 107 Map<String, ClassReader> zipClasses = new TreeMap<>(); 108 Map<String, InputStream> filesFound = new TreeMap<>(); 109 110 getDefaultAnalyzer().parseZip(MOCK_ANDROID_JAR, zipClasses, filesFound); 111 TreeMap<String, ClassReader> found = new TreeMap<>(); 112 113 ClassReader cr = AsmAnalyzer.findClass("mock_android.view.ViewGroup$LayoutParams", 114 zipClasses, found); 115 116 assertNotNull(cr); 117 assertEquals("mock_android/view/ViewGroup$LayoutParams", cr.getClassName()); 118 assertArrayEquals(new String[] { "mock_android.view.ViewGroup$LayoutParams" }, 119 found.keySet().toArray()); 120 assertArrayEquals(new ClassReader[] { cr }, found.values().toArray()); 121 } 122 123 @Test testInclude()124 public void testInclude() throws IOException { 125 AsmAnalyzer analyzer = new AsmAnalyzer(new MockLog(), MOCK_ANDROID_JAR, null, 126 new String[] { 127 "mock_android.util.EmptyArray", // Single class select 128 "mock_android.fake.**", // Multi package select 129 "mock_android.fake2.*", // Exclude subpackages select 130 }, 131 DEFAULT_EXCLUDES, 132 DEFAULT_INCLUDE_FILES, 133 new MethodReplacer[] {}); 134 Result result = analyzer.analyze(); 135 assertArrayEquals(new String[] { 136 "mock_android.fake.InnerTest$NotStaticInner1", 137 "mock_android.fake.FakeClass", 138 "mock_android.util.EmptyArray", 139 "mock_android.fake2.FakeClass", 140 "mock_android.fake.InnerTest$DerivingClass", 141 "mock_android.fake.InnerTest$NotStaticInner2", 142 "mock_android.fake.subpackage.SubpackageClassC$InnerClass", 143 "mock_android.fake.InnerTest$MyGenerics1", 144 "mock_android.fake.InnerTest$MyIntEnum", 145 "mock_android.fake.InnerTest$MyStaticInnerClass", 146 "mock_android.fake.InnerTest", 147 "mock_android.fake.subpackage.SubpackageClassB", 148 "mock_android.fake.subpackage.SubpackageClassA", 149 "mock_android.fake.InnerTest$1", 150 "mock_android.fake.subpackage.SubpackageClassC$StaticInnerClass", 151 "mock_android.fake.subpackage.SubpackageClassC", 152 }, 153 result.getFound().keySet().toArray()); 154 } 155 156 @Test testFindClassesDerivingFrom()157 public void testFindClassesDerivingFrom() throws IOException { 158 Map<String, ClassReader> zipClasses = new TreeMap<>(); 159 Map<String, InputStream> filesFound = new TreeMap<>(); 160 161 getDefaultAnalyzer().parseZip(MOCK_ANDROID_JAR, zipClasses, filesFound); 162 TreeMap<String, ClassReader> found = new TreeMap<>(); 163 164 AsmAnalyzer.findClassesDerivingFrom("mock_android.view.View", zipClasses, found); 165 166 assertArrayEquals(new String[] { 167 "mock_android.view.View", 168 "mock_android.view.ViewGroup", 169 "mock_android.widget.LinearLayout", 170 "mock_android.widget.TableLayout", 171 }, 172 found.keySet().toArray()); 173 174 for (String key : found.keySet()) { 175 ClassReader value = found.get(key); 176 assertNotNull("No value for " + key, value); 177 assertEquals(key, AsmAnalyzer.classReaderToClassName(value)); 178 } 179 } 180 181 @Test testDependencyVisitor()182 public void testDependencyVisitor() throws IOException { 183 Map<String, ClassReader> zipClasses = new TreeMap<>(); 184 Map<String, InputStream> filesFound = new TreeMap<>(); 185 186 getDefaultAnalyzer().parseZip(MOCK_ANDROID_JAR, zipClasses, filesFound); 187 TreeMap<String, ClassReader> keep = new TreeMap<>(); 188 TreeMap<String, ClassReader> new_keep = new TreeMap<>(); 189 TreeMap<String, ClassReader> in_deps = new TreeMap<>(); 190 TreeMap<String, ClassReader> out_deps = new TreeMap<>(); 191 192 ClassReader cr = AsmAnalyzer.findClass("mock_android.widget.LinearLayout", zipClasses, keep); 193 DependencyVisitor visitor = getDefaultAnalyzer().getVisitor(zipClasses, keep, new_keep, in_deps, out_deps); 194 195 // get first level dependencies 196 cr.accept(visitor, 0 /* flags */); 197 198 assertArrayEquals(new String[] { 199 "mock_android.util.EmptyArray", 200 "mock_android.view.ViewGroup", 201 "mock_android.widget.LinearLayout$LayoutParams", 202 }, 203 out_deps.keySet().toArray()); 204 205 in_deps.putAll(out_deps); 206 out_deps.clear(); 207 208 // get second level dependencies 209 for (ClassReader cr2 : in_deps.values()) { 210 cr2.accept(visitor, 0 /* flags */); 211 } 212 213 assertArrayEquals(new String[] { 214 "mock_android.view.View", 215 "mock_android.view.ViewGroup$LayoutParams", 216 "mock_android.view.ViewGroup$MarginLayoutParams", 217 }, 218 out_deps.keySet().toArray()); 219 220 in_deps.putAll(out_deps); 221 out_deps.clear(); 222 223 // get third level dependencies (there are none) 224 for (ClassReader cr2 : in_deps.values()) { 225 cr2.accept(visitor, 0 /* flags */); 226 } 227 keep.putAll(new_keep); 228 229 assertArrayEquals(new String[] { }, out_deps.keySet().toArray()); 230 assertArrayEquals(new String[] { 231 "mock_android.widget.LinearLayout", 232 "notjava.lang.JavaClass", 233 }, keep.keySet().toArray()); 234 } 235 } 236