Add tests about over propagation of assume* rules.
Bug: 137938214
Change-Id: I934f96f37ca75c019a38ab5459c6b80f1537a7a4
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsVisibleMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsVisibleMethodsTest.java
new file mode 100644
index 0000000..a9dda0d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsVisibleMethodsTest.java
@@ -0,0 +1,245 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.assumenosideeffects;
+
+import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssumenosideeffectsVisibleMethodsTest extends TestBase {
+ private static final Class<?> MAIN = TestClass.class;
+
+ enum TestConfig {
+ RULE_THAT_REFERS_LIB_BASE,
+ RULE_THAT_REFERS_PRG_BASE,
+ RULE_THAT_REFERS_PRG_SUB,
+ RULE_WITH_EXTENDS_LIB_BASE,
+ RULE_WITH_EXTENDS_PRG_BASE;
+ // The remaining case(s) are not tested since no items are matched.
+ // RULE_WITH_EXTENDS_PRG_SUB;
+
+ public String getKeepRule() {
+ switch (this) {
+ case RULE_THAT_REFERS_LIB_BASE:
+ return StringUtils.lines(
+ "-assumenosideeffects class " + LibraryBase.class.getTypeName() + " {",
+ " *;",
+ "}");
+ case RULE_THAT_REFERS_PRG_BASE:
+ return StringUtils.lines(
+ "-assumenosideeffects class " + ProgramBase.class.getTypeName() + " {",
+ " *;",
+ "}");
+ case RULE_THAT_REFERS_PRG_SUB:
+ return StringUtils.lines(
+ "-assumenosideeffects class " + ProgramSub.class.getTypeName() + " {",
+ " *;",
+ "}");
+ case RULE_WITH_EXTENDS_LIB_BASE:
+ return StringUtils.lines(
+ "-assumenosideeffects class * extends " + LibraryBase.class.getTypeName() + " {",
+ " *;",
+ "}");
+ case RULE_WITH_EXTENDS_PRG_BASE:
+ return StringUtils.lines(
+ "-assumenosideeffects class * extends " + ProgramBase.class.getTypeName() + " {",
+ " *;",
+ "}");
+ }
+ throw new Unreachable();
+ }
+
+ public String expectedOutput() {
+ switch (this) {
+ case RULE_THAT_REFERS_LIB_BASE:
+ return StringUtils.lines(
+ "Unless LibraryBase is explicitly mentioned",
+ // TODO(b/137938214): Should not mark Program*'s methods.
+ // "Nah; no more throwing."
+ // "[ProgramBase]: as long as ProgramBase is specified"
+ // "No, no, no; no more throwing."
+ // "[ProgramSub]: as long as ProgramSub is specified"
+ "The end");
+ case RULE_THAT_REFERS_PRG_BASE:
+ return StringUtils.lines(
+ // TODO(b/137938214): Should not mark LibraryBase's methods.
+ // "Throwing!",
+ "Unless LibraryBase is explicitly mentioned",
+ // "[LibraryBase]: as long as LibraryBase is specified"
+ // TODO(b/137938214): Should we mark ProgramSub's methods?
+ // Those are clearly an instance of ProgramSub type, and invocations with it.
+ // One caveat: ProgramSub#throwing doesn't exist, so ProgramBase#throwing will be
+ // used after the resolution, which is specified as side effect free.
+ // "[ProgramSub]: as long as ProgramSub is specified"
+ "The end");
+ case RULE_THAT_REFERS_PRG_SUB:
+ return StringUtils.lines(
+ // TODO(b/137938214): Should not mark LibraryBase's methods.
+ // "Throwing!",
+ "Unless LibraryBase is explicitly mentioned",
+ // TODO(b/137938214): Should not mark *Base's #debug(...)
+ // "[LibraryBase]: as long as LibraryBase is specified"
+ // "[ProgramBase]: as long as ProgramBase is specified"
+ "The end");
+ case RULE_WITH_EXTENDS_LIB_BASE:
+ return StringUtils.lines(
+ // TODO(b/137938214): Should not mark LibraryBase's methods.
+ // "Throwing!",
+ "Unless LibraryBase is explicitly mentioned",
+ // "[LibraryBase]: as long as LibraryBase is specified"
+ "The end");
+ case RULE_WITH_EXTENDS_PRG_BASE:
+ return StringUtils.lines(
+ // TODO(b/137938214): Should not mark LibraryBase's methods.
+ // "Throwing!",
+ "Unless LibraryBase is explicitly mentioned",
+ // "[LibraryBase]: as long as LibraryBase is specified"
+ "The end");
+ }
+ throw new Unreachable();
+ }
+ }
+
+ @Parameterized.Parameters(name = "{0} {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(getTestParameters().withAllRuntimes().build(), TestConfig.values());
+ }
+
+ private final TestParameters parameters;
+ private final TestConfig config;
+
+ public AssumenosideeffectsVisibleMethodsTest(TestParameters parameters, TestConfig config) {
+ this.parameters = parameters;
+ this.config = config;
+ }
+
+ @ClassRule
+ public static TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
+
+ private static Path libJarPath;
+ private static Path libDexPath;
+
+ @BeforeClass
+ public static void prepareLib() throws Exception {
+ libJarPath = staticTemp.newFile("lib.jar").toPath().toAbsolutePath();
+ writeClassesToJar(libJarPath, ImmutableList.of(LibraryBase.class));
+ libDexPath = staticTemp.newFile("lib.dex").toPath().toAbsolutePath();
+ testForD8(staticTemp)
+ .addProgramFiles(libJarPath)
+ .setMinApi(AndroidApiLevel.B)
+ .setProgramConsumer(new ArchiveConsumer(libDexPath))
+ .compile();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(libJarPath)
+ .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+ .addProgramClasses(ProgramBase.class, ProgramSub.class, MAIN)
+ .addKeepMainRule(MAIN)
+ .addKeepRules(config.getKeepRule())
+ .noMinification()
+ .enableMergeAnnotations()
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getRuntime())
+ .compile()
+ .addRunClasspathFiles(parameters.isDexRuntime() ? libDexPath : libJarPath)
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutput(config.expectedOutput());
+ }
+
+ static class LibraryBase {
+ protected void throwing(String message) {
+ throw new RuntimeException(message);
+ }
+
+ protected void debug(String message) {
+ System.out.println("[LibraryBase]: " + message);
+ }
+ }
+
+ @NeverClassInline
+ @NeverMerge
+ static class ProgramBase extends LibraryBase {
+ @NeverInline
+ @Override
+ protected void throwing(String message) {
+ System.out.println(message + "; no more throwing.");
+ }
+
+ @NeverInline
+ @Override
+ protected void debug(String message) {
+ System.out.println("[ProgramBase]: " + message);
+ }
+ }
+
+ @NeverClassInline
+ static class ProgramSub extends ProgramBase {
+ @NeverInline
+ @Override
+ protected void debug(String message) {
+ System.out.println("[ProgramSub]: " + message);
+ }
+ }
+
+ static class TestClass {
+ @NeverInline
+ static LibraryBase createLibraryInstance() {
+ return System.currentTimeMillis() > 0 ? new LibraryBase() : new ProgramBase();
+ }
+
+ @NeverInline
+ static ProgramBase createProgramInstance() {
+ return System.currentTimeMillis() > 0 ? new ProgramBase() : new ProgramSub();
+ }
+
+ public static void main(String... args) {
+ LibraryBase libInstance = createLibraryInstance();
+ try {
+ libInstance.throwing("Throwing!");
+ System.out.println("Unless LibraryBase is explicitly mentioned");
+ } catch (RuntimeException e) {
+ System.out.println("Expect to catch RuntimeException");
+ }
+ libInstance.debug("as long as LibraryBase is specified");
+
+ ProgramBase prgInstance = createProgramInstance();
+ try {
+ prgInstance.throwing("Nah");
+ } catch (RuntimeException e) {
+ System.out.println("Won't be any RuntimeException");
+ }
+ prgInstance.debug("as long as ProgramBase is specified");
+
+ ProgramSub subInstance = new ProgramSub();
+ try {
+ subInstance.throwing("No, no, no");
+ } catch (RuntimeException e) {
+ System.out.println("Won't be any RuntimeException");
+ }
+ subInstance.debug("as long as ProgramSub is specified");
+ System.out.println("The end");
+ }
+ }
+}