Add test for "TreeShaking: Detect instance fields never written"

Bug: 80455722
Change-Id: I65de4c34eafb982fa85f7081613ed7d71b3e959a
diff --git a/src/test/examples/shaking18/Base.java b/src/test/examples/shaking18/Base.java
new file mode 100644
index 0000000..f5707d1
--- /dev/null
+++ b/src/test/examples/shaking18/Base.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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 shaking18;
+
+public abstract class Base {
+
+  public abstract String getMessage();
+}
diff --git a/src/test/examples/shaking18/Derived1.java b/src/test/examples/shaking18/Derived1.java
new file mode 100644
index 0000000..c80d5e0
--- /dev/null
+++ b/src/test/examples/shaking18/Derived1.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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 shaking18;
+
+public class Derived1 extends Base {
+
+  @Override
+  public String getMessage() {
+    return "Hello from Derived1";
+  }
+}
diff --git a/src/test/examples/shaking18/Derived2.java b/src/test/examples/shaking18/Derived2.java
new file mode 100644
index 0000000..9fd8ec4
--- /dev/null
+++ b/src/test/examples/shaking18/Derived2.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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 shaking18;
+
+public class Derived2 extends Base {
+
+  @Override
+  public String getMessage() {
+    return "Hello from Derived2";
+  }
+}
diff --git a/src/test/examples/shaking18/DerivedUnused.java b/src/test/examples/shaking18/DerivedUnused.java
new file mode 100644
index 0000000..cdcf50d
--- /dev/null
+++ b/src/test/examples/shaking18/DerivedUnused.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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 shaking18;
+
+public class DerivedUnused extends Base {
+
+  @Override
+  public String getMessage() {
+    return "Hello from DerivedUnused";
+  }
+}
diff --git a/src/test/examples/shaking18/Options.java b/src/test/examples/shaking18/Options.java
new file mode 100644
index 0000000..7012340
--- /dev/null
+++ b/src/test/examples/shaking18/Options.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2018, 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 shaking18;
+
+public class Options {
+  public boolean alwaysFalse = false;
+  public boolean dummy = false;
+
+  public Options() {}
+}
diff --git a/src/test/examples/shaking18/Shaking.java b/src/test/examples/shaking18/Shaking.java
new file mode 100644
index 0000000..73ba5df
--- /dev/null
+++ b/src/test/examples/shaking18/Shaking.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2018, 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 shaking18;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    int i = args.length;
+    run(i);
+  }
+
+  private static void run(int i) {
+    // Three invocations of each method to avoid inlining.
+    Options o =
+        i % 2 == 0 ? getOptions(i % 3) : i % 5 == 0 ? getOptions(i % 7) : getOptions(i % 11);
+    print(make(o, i % 13));
+    print(make(o, i % 17));
+    print(make(o, i % 19));
+  }
+
+  private static Base make(Options o, int i) {
+    if (o.alwaysFalse) {
+      return new DerivedUnused();
+    }
+    return o.dummy ? new Derived1() : new Derived2();
+  }
+
+  private static Options getOptions(int i) {
+    Options o = new Options();
+    o.dummy = i % 101 < 23;
+    return o;
+  }
+
+  private static void print(Base b) {
+    System.out.println(b.getMessage());
+  }
+}
diff --git a/src/test/examples/shaking18/keep-rules.txt b/src/test/examples/shaking18/keep-rules.txt
new file mode 100644
index 0000000..18a2ae0
--- /dev/null
+++ b/src/test/examples/shaking18/keep-rules.txt
@@ -0,0 +1,9 @@
+# Copyright (c) 2017, 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking18.Shaking {
+  public static void main(...);
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
new file mode 100644
index 0000000..97d9bfc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2018, 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.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.DexInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShaking18Test extends TreeShakingTest {
+
+  @Parameters(name = "mode:{0}-{1} minify:{2}")
+  public static Collection<Object[]> data() {
+    List<Object[]> parameters = new ArrayList<>();
+    for (MinifyMode minify : MinifyMode.values()) {
+      parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+      parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+      parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+    }
+    return parameters;
+  }
+
+  public TreeShaking18Test(Frontend frontend, Backend backend, MinifyMode minify) {
+    super("examples/shaking18", "shaking18.Shaking", frontend, backend, minify);
+  }
+
+  @Test
+  public void test() throws Exception {
+    runTest(
+        TreeShaking18Test::unusedRemoved,
+        null,
+        null,
+        ImmutableList.of("src/test/examples/shaking18/keep-rules.txt"));
+  }
+
+  private static void unusedRemoved(DexInspector inspector) {
+    // TODO(b/80455722): Change to assertFalse when tree-shaking detects this case.
+    Assert.assertTrue(
+        "DerivedUnused should be removed", inspector.clazz("shaking18.DerivedUnused").isPresent());
+  }
+}