1/*
2 * Copyright (C) 2008 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
18package com.android.tools.layoutlib.create;
19
20import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
21
22import org.junit.Before;
23import org.junit.Test;
24import org.objectweb.asm.ClassReader;
25
26import java.io.IOException;
27import java.io.InputStream;
28import java.net.URL;
29import java.util.ArrayList;
30import java.util.Collections;
31import java.util.Map;
32import java.util.Set;
33import java.util.TreeMap;
34
35import static org.junit.Assert.assertArrayEquals;
36import static org.junit.Assert.assertEquals;
37import static org.junit.Assert.assertNotNull;
38
39/**
40 * Unit tests for some methods of {@link AsmAnalyzer}.
41 */
42public class AsmAnalyzerTest {
43
44    private MockLog mLog;
45    private ArrayList<String> mOsJarPath;
46    private AsmAnalyzer mAa;
47
48    @Before
49    public void setUp() throws Exception {
50        mLog = new MockLog();
51        URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
52
53        mOsJarPath = new ArrayList<>();
54        //noinspection ConstantConditions
55        mOsJarPath.add(url.getFile());
56
57        Set<String> excludeClasses = Collections.singleton("java.lang.JavaClass");
58
59        String[] includeFiles = new String[]{"mock_android/data/data*"};
60        mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */, null /* deriveFrom */,
61                null /* includeGlobs */, excludeClasses, includeFiles);
62    }
63
64    @Test
65    public void testParseZip() throws IOException {
66
67        Map<String, ClassReader> map = new TreeMap<>();
68        Map<String, InputStream> filesFound = new TreeMap<>();
69
70        mAa.parseZip(mOsJarPath, map, filesFound);
71
72        assertArrayEquals(new String[] {
73                "java.lang.JavaClass",
74                "mock_android.dummy.InnerTest",
75                "mock_android.dummy.InnerTest$DerivingClass",
76                "mock_android.dummy.InnerTest$MyGenerics1",
77                "mock_android.dummy.InnerTest$MyIntEnum",
78                "mock_android.dummy.InnerTest$MyStaticInnerClass",
79                "mock_android.dummy.InnerTest$NotStaticInner1",
80                "mock_android.dummy.InnerTest$NotStaticInner2",
81                "mock_android.util.EmptyArray",
82                "mock_android.view.View",
83                "mock_android.view.ViewGroup",
84                "mock_android.view.ViewGroup$LayoutParams",
85                "mock_android.view.ViewGroup$MarginLayoutParams",
86                "mock_android.widget.LinearLayout",
87                "mock_android.widget.LinearLayout$LayoutParams",
88                "mock_android.widget.TableLayout",
89                "mock_android.widget.TableLayout$LayoutParams"
90            },
91            map.keySet().toArray());
92        assertArrayEquals(new String[] {"mock_android/data/dataFile"},
93            filesFound.keySet().toArray());
94    }
95
96    @Test
97    public void testFindClass() throws IOException, LogAbortException {
98
99        Map<String, ClassReader> zipClasses = new TreeMap<>();
100        Map<String, InputStream> filesFound = new TreeMap<>();
101
102        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
103        TreeMap<String, ClassReader> found = new TreeMap<>();
104
105        ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams",
106                zipClasses, found);
107
108        assertNotNull(cr);
109        assertEquals("mock_android/view/ViewGroup$LayoutParams", cr.getClassName());
110        assertArrayEquals(new String[] { "mock_android.view.ViewGroup$LayoutParams" },
111                found.keySet().toArray());
112        assertArrayEquals(new ClassReader[] { cr }, found.values().toArray());
113    }
114
115    @Test
116    public void testFindGlobs() throws IOException, LogAbortException {
117
118        Map<String, ClassReader> zipClasses = new TreeMap<>();
119        Map<String, InputStream> filesFound = new TreeMap<>();
120
121        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
122        TreeMap<String, ClassReader> found = new TreeMap<>();
123
124        // this matches classes, a package match returns nothing
125        found.clear();
126        mAa.findGlobs("mock_android.view", zipClasses, found);
127
128        assertArrayEquals(new String[] { },
129            found.keySet().toArray());
130
131        // a complex glob search. * is a search pattern that matches names, not dots
132        mAa.findGlobs("mock_android.*.*Group$*Layout*", zipClasses, found);
133
134        assertArrayEquals(new String[] {
135                "mock_android.view.ViewGroup$LayoutParams",
136                "mock_android.view.ViewGroup$MarginLayoutParams"
137            },
138            found.keySet().toArray());
139
140        // a complex glob search. ** is a search pattern that matches names including dots
141        mAa.findGlobs("mock_android.**Group*", zipClasses, found);
142
143        assertArrayEquals(new String[] {
144                "mock_android.view.ViewGroup",
145                "mock_android.view.ViewGroup$LayoutParams",
146                "mock_android.view.ViewGroup$MarginLayoutParams"
147            },
148            found.keySet().toArray());
149
150        // matches a single class
151        found.clear();
152        mAa.findGlobs("mock_android.view.View", zipClasses, found);
153
154        assertArrayEquals(new String[] {
155                "mock_android.view.View"
156            },
157            found.keySet().toArray());
158
159        // matches everyting inside the given package but not sub-packages
160        found.clear();
161        mAa.findGlobs("mock_android.view.*", zipClasses, found);
162
163        assertArrayEquals(new String[] {
164                "mock_android.view.View",
165                "mock_android.view.ViewGroup",
166                "mock_android.view.ViewGroup$LayoutParams",
167                "mock_android.view.ViewGroup$MarginLayoutParams"
168            },
169            found.keySet().toArray());
170
171        for (String key : found.keySet()) {
172            ClassReader value = found.get(key);
173            assertNotNull("No value for " + key, value);
174            assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
175        }
176    }
177
178    @Test
179    public void testFindClassesDerivingFrom() throws LogAbortException, IOException {
180
181        Map<String, ClassReader> zipClasses = new TreeMap<>();
182        Map<String, InputStream> filesFound = new TreeMap<>();
183
184        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
185        TreeMap<String, ClassReader> found = new TreeMap<>();
186
187        mAa.findClassesDerivingFrom("mock_android.view.View", zipClasses, found);
188
189        assertArrayEquals(new String[] {
190                "mock_android.view.View",
191                "mock_android.view.ViewGroup",
192                "mock_android.widget.LinearLayout",
193                "mock_android.widget.TableLayout",
194            },
195            found.keySet().toArray());
196
197        for (String key : found.keySet()) {
198            ClassReader value = found.get(key);
199            assertNotNull("No value for " + key, value);
200            assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
201        }
202    }
203
204    @Test
205    public void testDependencyVisitor() throws IOException, LogAbortException {
206
207        Map<String, ClassReader> zipClasses = new TreeMap<>();
208        Map<String, InputStream> filesFound = new TreeMap<>();
209
210        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
211        TreeMap<String, ClassReader> keep = new TreeMap<>();
212        TreeMap<String, ClassReader> new_keep = new TreeMap<>();
213        TreeMap<String, ClassReader> in_deps = new TreeMap<>();
214        TreeMap<String, ClassReader> out_deps = new TreeMap<>();
215
216        ClassReader cr = mAa.findClass("mock_android.widget.LinearLayout", zipClasses, keep);
217        DependencyVisitor visitor = mAa.getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
218
219        // get first level dependencies
220        cr.accept(visitor, 0 /* flags */);
221
222        assertArrayEquals(new String[] {
223                "mock_android.util.EmptyArray",
224                "mock_android.view.ViewGroup",
225                "mock_android.widget.LinearLayout$LayoutParams",
226            },
227            out_deps.keySet().toArray());
228
229        in_deps.putAll(out_deps);
230        out_deps.clear();
231
232        // get second level dependencies
233        for (ClassReader cr2 : in_deps.values()) {
234            cr2.accept(visitor, 0 /* flags */);
235        }
236
237        assertArrayEquals(new String[] {
238                "mock_android.view.View",
239                "mock_android.view.ViewGroup$LayoutParams",
240                "mock_android.view.ViewGroup$MarginLayoutParams",
241            },
242            out_deps.keySet().toArray());
243
244        in_deps.putAll(out_deps);
245        out_deps.clear();
246
247        // get third level dependencies (there are none)
248        for (ClassReader cr2 : in_deps.values()) {
249            cr2.accept(visitor, 0 /* flags */);
250        }
251        keep.putAll(new_keep);
252
253        assertArrayEquals(new String[] { }, out_deps.keySet().toArray());
254        assertArrayEquals(new String[] {
255                "mock_android.widget.LinearLayout",
256        }, keep.keySet().toArray());
257    }
258}
259