• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 
5 package com.android.tools.r8;
6 
7 import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
8 import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
9 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
10 import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertTrue;
13 
14 import com.android.tools.r8.ToolHelper.DexVm;
15 import com.android.tools.r8.errors.CompilationError;
16 import com.android.tools.r8.utils.DexInspector;
17 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
18 import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
19 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
20 import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
21 import com.android.tools.r8.utils.InternalOptions;
22 import com.android.tools.r8.utils.OffOrAuto;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.function.Consumer;
32 import java.util.function.Predicate;
33 import java.util.function.UnaryOperator;
34 import org.junit.Assert;
35 import org.junit.Rule;
36 import org.junit.Test;
37 import org.junit.rules.ExpectedException;
38 import org.junit.rules.TemporaryFolder;
39 
40 public abstract class RunExamplesAndroidOTest<B> {
41   static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
42 
43   abstract class TestRunner {
44     final String testName;
45     final String packageName;
46     final String mainClass;
47 
48     final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
49     final List<Consumer<DexInspector>> dexInspectorChecks = new ArrayList<>();
50     final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
51 
TestRunner(String testName, String packageName, String mainClass)52     TestRunner(String testName, String packageName, String mainClass) {
53       this.testName = testName;
54       this.packageName = packageName;
55       this.mainClass = mainClass;
56     }
57 
withDexCheck(Consumer<DexInspector> check)58     TestRunner withDexCheck(Consumer<DexInspector> check) {
59       dexInspectorChecks.add(check);
60       return this;
61     }
62 
withClassCheck(Consumer<FoundClassSubject> check)63     TestRunner withClassCheck(Consumer<FoundClassSubject> check) {
64       withDexCheck(inspector -> inspector.forAllClasses(check));
65       return this;
66     }
67 
withMethodCheck(Consumer<FoundMethodSubject> check)68     TestRunner withMethodCheck(Consumer<FoundMethodSubject> check) {
69       withClassCheck(clazz -> clazz.forAllMethods(check));
70       return this;
71     }
72 
73     <T extends InstructionSubject> TestRunner
withInstructionCheck(Predicate<InstructionSubject> filter, Consumer<T> check)74     withInstructionCheck(Predicate<InstructionSubject> filter, Consumer<T> check) {
75       withMethodCheck(method -> {
76         if (method.isAbstract()) {
77           return;
78         }
79         Iterator<T> iterator = method.iterateInstructions(filter);
80         while (iterator.hasNext()) {
81           check.accept(iterator.next());
82         }
83       });
84       return this;
85     }
86 
withOptionConsumer(Consumer<InternalOptions> consumer)87     TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) {
88       optionConsumers.add(consumer);
89       return this;
90     }
91 
withInterfaceMethodDesugaring(OffOrAuto behavior)92     TestRunner withInterfaceMethodDesugaring(OffOrAuto behavior) {
93       return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
94     }
95 
withTryWithResourcesDesugaring(OffOrAuto behavior)96     TestRunner withTryWithResourcesDesugaring(OffOrAuto behavior) {
97       return withOptionConsumer(o -> o.tryWithResourcesDesugaring = behavior);
98     }
99 
withBuilderTransformation(UnaryOperator<B> builderTransformation)100     TestRunner withBuilderTransformation(UnaryOperator<B> builderTransformation) {
101       builderTransformations.add(builderTransformation);
102       return this;
103     }
104 
combinedOptionConsumer(InternalOptions options)105     void combinedOptionConsumer(InternalOptions options) {
106       for (Consumer<InternalOptions> consumer : optionConsumers) {
107         consumer.accept(options);
108       }
109     }
110 
run()111     void run() throws Throwable {
112       if (compilationErrorExpected(testName)) {
113         thrown.expect(CompilationError.class);
114       }
115 
116       String qualifiedMainClass = packageName + "." + mainClass;
117       Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
118       Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
119 
120       build(inputFile, out);
121 
122       if (!ToolHelper.artSupported()) {
123         return;
124       }
125 
126       boolean expectedToFail = expectedToFail(testName);
127       if (expectedToFail) {
128         thrown.expect(Throwable.class);
129       }
130 
131       if (!dexInspectorChecks.isEmpty()) {
132         DexInspector inspector = new DexInspector(out);
133         for (Consumer<DexInspector> check : dexInspectorChecks) {
134           check.accept(inspector);
135         }
136       }
137 
138       String output = ToolHelper.runArtNoVerificationErrors(out.toString(), qualifiedMainClass);
139       if (!expectedToFail) {
140         ToolHelper.ProcessResult javaResult =
141             ToolHelper.runJava(ImmutableList.of(inputFile.toString()), qualifiedMainClass);
142         assertEquals("JVM run failed", javaResult.exitCode, 0);
143         assertTrue(
144             "JVM output does not match art output.\n\tjvm: "
145                 + javaResult.stdout
146                 + "\n\tart: "
147                 + output,
148             output.equals(javaResult.stdout));
149       }
150     }
151 
withMinApiLevel(int minApiLevel)152     abstract TestRunner withMinApiLevel(int minApiLevel);
153 
build(Path inputFile, Path out)154     abstract void build(Path inputFile, Path out) throws Throwable;
155   }
156 
157   private static List<String> compilationErrorExpected =
158       ImmutableList.of(
159           "invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
160 
161   private static Map<DexVm, List<String>> failsOn =
162       ImmutableMap.of(
163           DexVm.ART_4_4_4, ImmutableList.of(
164               // API not supported
165               "paramnames",
166               "repeat_annotations_new_api",
167               // Dex version not supported
168               "invokepolymorphic",
169               "invokecustom"
170           ),
171           DexVm.ART_5_1_1, ImmutableList.of(
172               // API not supported
173               "paramnames",
174               "repeat_annotations_new_api",
175               // Dex version not supported
176               "invokepolymorphic",
177               "invokecustom"
178           ),
179           DexVm.ART_6_0_1, ImmutableList.of(
180               // API not supported
181               "paramnames",
182               "repeat_annotations_new_api",
183               // Dex version not supported
184               "invokepolymorphic",
185               "invokecustom"
186           ),
187           DexVm.ART_7_0_0, ImmutableList.of(
188               // API not supported
189               "paramnames",
190               // Dex version not supported
191               "invokepolymorphic",
192               "invokecustom"
193           ),
194           DexVm.ART_DEFAULT, ImmutableList.of(
195           )
196       );
197 
198   @Rule
199   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
200 
201   @Rule
202   public ExpectedException thrown = ExpectedException.none();
203 
failsOn(Map<ToolHelper.DexVm, List<String>> failsOn, String name)204   boolean failsOn(Map<ToolHelper.DexVm, List<String>> failsOn, String name) {
205     return failsOn.containsKey(ToolHelper.getDexVm())
206         && failsOn.get(ToolHelper.getDexVm()).contains(name);
207   }
208 
expectedToFail(String name)209   boolean expectedToFail(String name) {
210     return failsOn(failsOn, name);
211   }
212 
compilationErrorExpected(String testName)213   boolean compilationErrorExpected(String testName) {
214     return compilationErrorExpected.contains(testName);
215   }
216 
217   @Test
invokeCustom()218   public void invokeCustom() throws Throwable {
219     test("invokecustom", "invokecustom", "InvokeCustom")
220         .withMinApiLevel(ANDROID_O_API)
221         .run();
222   }
223 
224   @Test
invokeCustomErrorDueToMinSdk()225   public void invokeCustomErrorDueToMinSdk() throws Throwable {
226     test("invokecustom-error-due-to-min-sdk", "invokecustom", "InvokeCustom")
227         .withMinApiLevel(25)
228         .run();
229   }
230 
231   @Test
invokePolymorphic()232   public void invokePolymorphic() throws Throwable {
233     test("invokepolymorphic", "invokepolymorphic", "InvokePolymorphic")
234         .withMinApiLevel(ANDROID_O_API)
235         .run();
236   }
237 
238   @Test
invokePolymorphicErrorDueToMinSdk()239   public void invokePolymorphicErrorDueToMinSdk() throws Throwable {
240     test("invokepolymorphic-error-due-to-min-sdk", "invokepolymorphic", "InvokePolymorphic")
241         .withMinApiLevel(25)
242         .run();
243   }
244 
245   @Test
lambdaDesugaring()246   public void lambdaDesugaring() throws Throwable {
247     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
248         .withMinApiLevel(ANDROID_K_API)
249         .run();
250   }
251 
252   @Test
lambdaDesugaringNPlus()253   public void lambdaDesugaringNPlus() throws Throwable {
254     test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
255         .withMinApiLevel(ANDROID_K_API)
256         .withInterfaceMethodDesugaring(OffOrAuto.Auto)
257         .run();
258   }
259 
260   @Test
lambdaDesugaringValueAdjustments()261   public void lambdaDesugaringValueAdjustments() throws Throwable {
262     test("lambdadesugaring-value-adjustments", "lambdadesugaring", "ValueAdjustments")
263         .withMinApiLevel(ANDROID_K_API)
264         .run();
265   }
266 
267   @Test
paramNames()268   public void paramNames() throws Throwable {
269     test("paramnames", "paramnames", "ParameterNames")
270         .withMinApiLevel(26)
271         .withOptionConsumer((internalOptions) -> internalOptions.allowParameterName = true)
272         .run();
273   }
274 
275   @Test
repeatAnnotationsNewApi()276   public void repeatAnnotationsNewApi() throws Throwable {
277     // No need to specify minSdk as repeat annotations are handled by javac and we do not have
278     // to do anything to support them. The library methods to access them just have to be in
279     // the system.
280     test("repeat_annotations_new_api", "repeat_annotations", "RepeatAnnotationsNewApi").run();
281   }
282 
283   @Test
repeatAnnotations()284   public void repeatAnnotations() throws Throwable {
285     // No need to specify minSdk as repeat annotations are handled by javac and we do not have
286     // to do anything to support them. The library methods to access them just have to be in
287     // the system.
288     test("repeat_annotations", "repeat_annotations", "RepeatAnnotations").run();
289   }
290 
291   @Test
testTryWithResources()292   public void testTryWithResources() throws Throwable {
293     test("try-with-resources-simplified", "trywithresources", "TryWithResourcesNotDesugaredTests")
294         .withTryWithResourcesDesugaring(OffOrAuto.Off)
295         .run();
296   }
297 
298   @Test
testTryWithResourcesDesugared()299   public void testTryWithResourcesDesugared() throws Throwable {
300     test("try-with-resources-simplified", "trywithresources", "TryWithResourcesDesugaredTests")
301         .withTryWithResourcesDesugaring(OffOrAuto.Auto)
302         .withInstructionCheck(InstructionSubject::isInvoke,
303             (InvokeInstructionSubject invoke) -> {
304               Assert.assertFalse(invoke.invokedMethod().name.toString().equals("addSuppressed"));
305               Assert.assertFalse(invoke.invokedMethod().name.toString().equals("getSuppressed"));
306             })
307         .run();
308   }
309 
test(String testName, String packageName, String mainClass)310   abstract TestRunner test(String testName, String packageName, String mainClass);
311 }
312