1 /*
2  * Copyright (C) 2014 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 import java.lang.reflect.InvocationTargetException;
18 import java.lang.reflect.Method;
19 import java.lang.reflect.Modifier;
20 import java.util.LinkedList;
21 import java.util.List;
22 
23 /**
24  * Smali excercise.
25  */
26 public class Main {
27 
28     private static class TestCase {
TestCase(String testName, String testClass, String testMethodName, Object[] values, Throwable expectedException, Object expectedReturn, boolean checkCompiled)29         public TestCase(String testName, String testClass, String testMethodName, Object[] values,
30                         Throwable expectedException, Object expectedReturn,
31                         boolean checkCompiled) {
32             this.testName = testName;
33             this.testClass = testClass;
34             this.testMethodName = testMethodName;
35             this.values = values;
36             this.expectedException = expectedException;
37             this.expectedReturn = expectedReturn;
38             this.checkCompiled = checkCompiled;
39         }
40 
TestCase(String testName, String testClass, String testMethodName, Object[] values, Throwable expectedException, Object expectedReturn)41         public TestCase(String testName, String testClass, String testMethodName, Object[] values,
42                         Throwable expectedException, Object expectedReturn) {
43             this(testName, testClass, testMethodName, values, expectedException,
44                  expectedReturn, false);
45         }
46 
47         String testName;
48         String testClass;
49         String testMethodName;
50         Object[] values;
51         Throwable expectedException;
52         Object expectedReturn;
53         boolean checkCompiled;
54     }
55 
56     private List<TestCase> testCases;
57 
Main()58     public Main() {
59         // Create the test cases.
60         testCases = new LinkedList<TestCase>();
61         testCases.add(new TestCase("PackedSwitch", "PackedSwitch", "packedSwitch",
62                 new Object[]{123}, null, 123));
63         testCases.add(new TestCase("PackedSwitch key INT_MAX", "PackedSwitch",
64                 "packedSwitch_INT_MAX", new Object[]{123}, null, 123));
65         testCases.add(new TestCase("PackedSwitch key overflow", "b_24399945",
66                 "packedSwitch_overflow", new Object[]{123}, new VerifyError(), null));
67 
68         testCases.add(new TestCase("b/17790197", "B17790197", "getInt", null, null, 100));
69         testCases.add(new TestCase("FloatBadArgReg", "FloatBadArgReg", "getInt",
70                 new Object[]{100}, null, 100));
71         testCases.add(new TestCase("negLong", "negLong", "negLong", null, null, 122142L));
72         testCases.add(new TestCase("sameFieldNames", "sameFieldNames", "getInt", null, null, 7));
73         testCases.add(new TestCase("b/18380491", "B18380491ConcreteClass", "foo",
74                 new Object[]{42}, null, 42));
75         testCases.add(new TestCase("invoke-super abstract", "B18380491ConcreteClass", "foo",
76                 new Object[]{0}, new AbstractMethodError(), null));
77         testCases.add(new TestCase("BadCaseInOpRegRegReg", "BadCaseInOpRegRegReg", "getInt", null,
78                 null, 2));
79         testCases.add(new TestCase("CmpLong", "CmpLong", "run", null, null, 0));
80         testCases.add(new TestCase("FloatIntConstPassing", "FloatIntConstPassing", "run", null,
81                 null, 2));
82         testCases.add(new TestCase("b/18718277", "B18718277", "getInt", null, null, 0));
83         testCases.add(new TestCase("b/18800943 (1)", "B18800943_1", "n_a", null, new VerifyError(),
84                 0));
85         testCases.add(new TestCase("b/18800943 (2)", "B18800943_2", "n_a", null, new VerifyError(),
86                 0));
87         testCases.add(new TestCase("MoveExc", "MoveExc", "run", null, new ArithmeticException(),
88                 null));
89         testCases.add(new TestCase("MoveExceptionOnEntry", "MoveExceptionOnEntry",
90             "moveExceptionOnEntry", new Object[]{0}, new VerifyError(), null));
91         testCases.add(new TestCase("EmptySparseSwitch", "EmptySparseSwitch", "run", null, null,
92                 null));
93         testCases.add(new TestCase("b/20224106", "B20224106", "run", null, new VerifyError(),
94                 0));
95         testCases.add(new TestCase("b/17410612", "B17410612", "run", null, new VerifyError(),
96                 0));
97         testCases.add(new TestCase("b/21863767", "B21863767", "run", null, null,
98                 null));
99         testCases.add(new TestCase("b/21873167", "B21873167", "test", null, null, null));
100         testCases.add(new TestCase("b/21614284", "B21614284", "test", new Object[] { null },
101                 new NullPointerException(), null));
102         testCases.add(new TestCase("b/21902684", "B21902684", "test", null, null, null));
103         testCases.add(new TestCase("b/22045582", "B22045582", "run", null, new VerifyError(),
104                 0));
105         testCases.add(new TestCase("b/22045582 (int)", "B22045582Int", "run", null,
106                 new VerifyError(), 0));
107         testCases.add(new TestCase("b/22045582 (wide)", "B22045582Wide", "run", null,
108                 new VerifyError(), 0));
109         testCases.add(new TestCase("b/21886894", "B21886894", "test", null, new VerifyError(),
110                 null));
111         testCases.add(new TestCase("b/22080519", "B22080519", "run", null,
112                 new NullPointerException(), null));
113         testCases.add(new TestCase("b/21645819", "B21645819", "run", new Object[] { null },
114                 null, null));
115         testCases.add(new TestCase("b/22244733", "B22244733", "run", new Object[] { "abc" },
116                 null, "abc"));
117         testCases.add(new TestCase("b/22331663", "B22331663", "run", new Object[] { false },
118                 null, null));
119         testCases.add(new TestCase("b/22331663 (pass)", "B22331663Pass", "run",
120                 new Object[] { false }, null, null));
121         testCases.add(new TestCase("b/22331663 (fail)", "B22331663Fail", "run",
122                 new Object[] { false }, new VerifyError(), null));
123         testCases.add(new TestCase("b/22411633 (1)", "B22411633_1", "run", new Object[] { false },
124                 null, null));
125         testCases.add(new TestCase("b/22411633 (2)", "B22411633_2", "run", new Object[] { false },
126                 new VerifyError(), null));
127         testCases.add(new TestCase("b/22411633 (3)", "B22411633_3", "run", new Object[] { false },
128                 null, null));
129         testCases.add(new TestCase("b/22411633 (4)", "B22411633_4", "run", new Object[] { false },
130                 new VerifyError(), null));
131         testCases.add(new TestCase("b/22411633 (5)", "B22411633_5", "run", new Object[] { false },
132                 null, null));
133         testCases.add(new TestCase("b/22777307", "B22777307", "run", null, new InstantiationError(),
134                 null));
135         testCases.add(new TestCase("b/22881413", "B22881413", "run", null, null, null));
136         testCases.add(new TestCase("b/20843113", "B20843113", "run", null, null, null));
137         testCases.add(new TestCase("b/23201502 (float)", "B23201502", "runFloat", null,
138                 new NullPointerException(), null));
139         testCases.add(new TestCase("b/23201502 (double)", "B23201502", "runDouble", null,
140                 new NullPointerException(), null));
141         testCases.add(new TestCase("b/23300986", "B23300986", "runAliasAfterEnter",
142                 new Object[] { new Object() }, null, null));
143         testCases.add(new TestCase("b/23300986 (2)", "B23300986", "runAliasBeforeEnter",
144                 new Object[] { new Object() }, null, null));
145         testCases.add(new TestCase("b/23502994 (if-eqz)", "B23502994", "runIF_EQZ",
146                 new Object[] { new Object() }, null, null));
147         testCases.add(new TestCase("b/23502994 (check-cast)", "B23502994", "runCHECKCAST",
148                 new Object[] { "abc" }, null, null));
149         testCases.add(new TestCase("b/25494456", "B25494456", "run", null, new VerifyError(),
150                 null));
151         testCases.add(new TestCase("b/21869691", "B21869691A", "run", null,
152                 new IncompatibleClassChangeError(), null));
153         testCases.add(new TestCase("b/26143249", "B26143249", "run", null,
154                 new AbstractMethodError(), null));
155         testCases.add(new TestCase("b/26579108", "B26579108", "run", null, new VerifyError(),
156                 null));
157         testCases.add(new TestCase("b/26594149 (1)", "B26594149_1", "run", null, new VerifyError(),
158                 null));
159         testCases.add(new TestCase("b/26594149 (2)", "B26594149_2", "run", null, new VerifyError(),
160                 null));
161         testCases.add(new TestCase("b/26594149 (3)", "B26594149_3", "run", null, new VerifyError(),
162                 null));
163         testCases.add(new TestCase("b/26594149 (4)", "B26594149_4", "run", null, new VerifyError(),
164                 null));
165         testCases.add(new TestCase("b/26594149 (5)", "B26594149_5", "run", null, null, null));
166         testCases.add(new TestCase("b/26594149 (6)", "B26594149_6", "run", null, new VerifyError(),
167                 null));
168         testCases.add(new TestCase("b/26594149 (7)", "B26594149_7", "run", null, new VerifyError(),
169                 null));
170         testCases.add(new TestCase("b/26594149 (8)", "B26594149_8", "run", null, new VerifyError(),
171                 null));
172         testCases.add(new TestCase("b/27148248", "B27148248", "run", null, new VerifyError(),
173                 null));
174         testCases.add(new TestCase("b/26965384", "B26965384", "run", null, new VerifyError(),
175                 null));
176         testCases.add(new TestCase("b/27799205 (1)", "B27799205Helper", "run1", null, null, null));
177         testCases.add(new TestCase("b/27799205 (2)", "B27799205Helper", "run2", null,
178                 new VerifyError(), null));
179         testCases.add(new TestCase("b/27799205 (3)", "B27799205Helper", "run3", null,
180                 new VerifyError(), null));
181         testCases.add(new TestCase("b/27799205 (4)", "B27799205Helper", "run4", null,
182                 new VerifyError(), null));
183         testCases.add(new TestCase("b/27799205 (5)", "B27799205Helper", "run5", null,
184                 new VerifyError(), null));
185         testCases.add(new TestCase("b/27799205 (6)", "B27799205Helper", "run6", null, null, null));
186         testCases.add(new TestCase("b/28187158", "B28187158", "run", new Object[] { null },
187                 new VerifyError(), null));
188         testCases.add(new TestCase("b/29778499 (1)", "B29778499_1", "run", null,
189                 new IncompatibleClassChangeError(), null));
190         testCases.add(new TestCase("b/29778499 (2)", "B29778499_2", "run", null,
191                 new IncompatibleClassChangeError(), null));
192         testCases.add(new TestCase("b/30458218", "B30458218", "run", null, null, null));
193         testCases.add(new TestCase("b/31313170", "B31313170", "run", null, null, 0));
194         testCases.add(new TestCase("ConstClassAliasing", "ConstClassAliasing", "run", null, null,
195                 null, true));
196         testCases.add(new TestCase("b/121191566", "B121191566", "run", new Object[] { "a" }, null,
197                 true, false));
198         testCases.add(new TestCase("b/122501785", "B122501785", "run", null, new VerifyError(),
199                 0));
200         testCases.add(new TestCase("b/134061982", "B134061982", "run", new Object[] { 0 },
201                 new NullPointerException(), 0));
202         testCases.add(new TestCase("b/134061982 (2)", "B134061982_2", "run", new Object[] { 0 },
203                 new VerifyError(), 0));
204         testCases.add(new TestCase("b/121245951", "B121245951", "run", new Object[] { true,
205                 new Object() }, new IllegalMonitorStateException(), 0));
206         testCases.add(new TestCase("b/121245951 (2)", "B121245951_2", "run", new Object[] { true,
207                 new Object() }, new VerifyError(), 0));
208         testCases.add(new TestCase("b/121245951 (3)", "B121245951_3", "run", new Object[] {
209                 new Object() }, new IllegalMonitorStateException(), 0));
210     }
211 
runTests()212     public void runTests() {
213         for (TestCase tc : testCases) {
214             System.out.println(tc.testName);
215             try {
216                 runTest(tc);
217             } catch (Exception exc) {
218                 exc.printStackTrace(System.out);
219             }
220         }
221     }
222 
runTest(TestCase tc)223     private void runTest(TestCase tc) throws Exception {
224         Exception errorReturn = null;
225         try {
226             Class<?> c = Class.forName(tc.testClass);
227 
228             Method[] methods = c.getDeclaredMethods();
229 
230             // For simplicity we assume that test methods are not overloaded. So searching by name
231             // will give us the method we need to run.
232             Method method = null;
233             for (Method m : methods) {
234                 if (m.getName().equals(tc.testMethodName)) {
235                     method = m;
236                     break;
237                 }
238             }
239 
240             if (method == null) {
241                 errorReturn = new IllegalArgumentException("Could not find test method " +
242                                                            tc.testMethodName + " in class " +
243                                                            tc.testClass + " for test " +
244                                                            tc.testName);
245             } else {
246                 Object retValue;
247                 if (Modifier.isStatic(method.getModifiers())) {
248                     retValue = method.invoke(null, tc.values);
249                 } else {
250                     retValue = method.invoke(method.getDeclaringClass().newInstance(), tc.values);
251                 }
252                 if (tc.expectedException != null) {
253                     errorReturn = new IllegalStateException("Expected an exception in test " +
254                                                             tc.testName);
255                 } else if (tc.expectedReturn == null && retValue != null) {
256                     errorReturn = new IllegalStateException("Expected a null result in test " +
257                                                             tc.testName + " got " + retValue);
258                 } else if (tc.expectedReturn != null &&
259                            (retValue == null || !tc.expectedReturn.equals(retValue))) {
260                     errorReturn = new IllegalStateException("Expected return " +
261                                                             tc.expectedReturn +
262                                                             ", but got " + retValue);
263                 } else if (tc.checkCompiled && compiledWithOptimizing() && !isAotVerified(c)) {
264                     errorReturn = new IllegalStateException("Expected method " + method.getName() +
265                             " of class " + c.getName() +
266                             " be verified in compile-time in test " + tc.testName);
267                 } else {
268                     // Expected result, do nothing.
269                 }
270             }
271         } catch (Throwable exc) {
272             if (tc.expectedException == null) {
273                 errorReturn = new IllegalStateException("Did not expect exception", exc);
274             } else if (exc instanceof InvocationTargetException && exc.getCause() != null &&
275                        exc.getCause().getClass().equals(tc.expectedException.getClass())) {
276                 // Expected exception is wrapped in InvocationTargetException.
277             } else if (!tc.expectedException.getClass().equals(exc.getClass())) {
278                 errorReturn = new IllegalStateException("Expected " +
279                                                         tc.expectedException.getClass().getName() +
280                                                         ", but got " + exc.getClass(), exc);
281             } else {
282                 // Expected exception, do nothing.
283             }
284         } finally {
285             if (errorReturn != null) {
286                 throw errorReturn;
287             }
288         }
289     }
290 
main(String[] args)291     public static void main(String[] args) throws Exception {
292         System.loadLibrary(args[0]);
293 
294         Main main = new Main();
295 
296         main.runTests();
297 
298         System.out.println("Done!");
299     }
300 
isAotVerified(Class<?> cls)301     private native static boolean isAotVerified(Class<?> cls);
compiledWithOptimizing()302     private native static boolean compiledWithOptimizing();
303 }
304