Add tests for (package-)naming.

These tests and keep-rules came from:
http://cs/android/toolchain/jack/jack-tests/tests/com/android/jack/shrob/
with the same numberings for tracking purpose.

Bug: 37764746
Change-Id: I4909cfa38d1e55980eb203622d0bd298cb68716d
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 3feae5f..6685da7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -87,6 +87,7 @@
   public final DexString assertionsDisabled = createString("$assertionsDisabled");
 
   public final DexString stringDescriptor = createString("Ljava/lang/String;");
+  public final DexString stringArrayDescriptor = createString("[Ljava/lang/String;");
   public final DexString objectDescriptor = createString("Ljava/lang/Object;");
   public final DexString classDescriptor = createString("Ljava/lang/Class;");
   public final DexString enumDescriptor = createString("Ljava/lang/Enum;");
@@ -124,6 +125,7 @@
   public final DexType boxedNumberType = createType(boxedNumberDescriptor);
 
   public final DexType stringType = createType(stringDescriptor);
+  public final DexType stringArrayType = createType(stringArrayDescriptor);
   public final DexType objectType = createType(objectDescriptor);
   public final DexType enumType = createType(enumDescriptor);
   public final DexType annotationType = createType(annotationDescriptor);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index d8d3b8d..29b791d 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -229,7 +229,8 @@
     }
 
     ClassNamingState(String packageName, String separator) {
-      this.packagePrefix = ("L" + packageName + (packageName.isEmpty() ? "" : separator))
+      this.packagePrefix = ("L" + DescriptorUtils.getPackageBinaryNameFromJavaType(packageName)
+          + (packageName.isEmpty() ? "" : separator))
           .toCharArray();
       this.dictionaryIterator = dictionary.iterator();
     }
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 025002d..7a4f8f6 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -131,5 +131,24 @@
           || virtualTargetHolder != null && virtualTargetHolder.isLibraryClass()
           || staticTargetHolder != null && staticTargetHolder.isLibraryClass();
     }
+
+    @Override
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      renaming.forEach((item, str) -> {
+        if (item instanceof DexType) {
+          builder.append("[c] ");
+        } else if (item instanceof DexMethod) {
+          builder.append("[m] ");
+        } else if (item instanceof DexField) {
+          builder.append("[f] ");
+        }
+        builder.append(item.toSourceString());
+        builder.append(" -> ");
+        builder.append(str.toSourceString());
+        builder.append('\n');
+      });
+      return builder.toString();
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 277ee0f..3e7c8dd 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -148,6 +148,15 @@
     return classDescriptor.substring(1, classDescriptor.length() - 1);
   }
 
+  /**
+   * Convert package name to a binary name.
+   *
+   * @param packageName a package name i.e., "java.lang"
+   * @return java pacakge name in a binary name format, i.e., java/lang
+   */
+  public static String getPackageBinaryNameFromJavaType(String packageName) {
+    return packageName.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR);
+  }
 
   /**
    * Convert a class binary name to a descriptor.
diff --git a/src/test/examples/naming001/A.java b/src/test/examples/naming001/A.java
new file mode 100644
index 0000000..092bcf7
--- /dev/null
+++ b/src/test/examples/naming001/A.java
@@ -0,0 +1,25 @@
+// 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.
+package naming001;
+
+public class A {
+
+  A() {
+  }
+
+  A(int i) {
+  }
+
+  static {
+    C.m();
+  }
+
+  void m() {
+  }
+
+  @SuppressWarnings("unused")
+  private void privateFunc() {
+  }
+}
+
diff --git a/src/test/examples/naming001/B.java b/src/test/examples/naming001/B.java
new file mode 100644
index 0000000..30e520c
--- /dev/null
+++ b/src/test/examples/naming001/B.java
@@ -0,0 +1,8 @@
+// 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.
+package naming001;
+
+public class B {
+}
+
diff --git a/src/test/examples/naming001/C.java b/src/test/examples/naming001/C.java
new file mode 100644
index 0000000..24cd8ec
--- /dev/null
+++ b/src/test/examples/naming001/C.java
@@ -0,0 +1,13 @@
+// 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.
+package naming001;
+
+public class C {
+  static void f() {
+  }
+
+  static void m() {
+  }
+}
+
diff --git a/src/test/examples/naming001/D.java b/src/test/examples/naming001/D.java
new file mode 100644
index 0000000..c1ec759
--- /dev/null
+++ b/src/test/examples/naming001/D.java
@@ -0,0 +1,21 @@
+// 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.
+package naming001;
+
+public class D {
+  public void keep() {
+    System.out.println();
+  }
+
+  public static void main(String[] args) {
+    D d = new E();
+    d.keep();
+  }
+
+  public static void main2(String[] args) {
+    D d = new D();
+    d.keep();
+  }
+}
+
diff --git a/src/test/examples/naming001/E.java b/src/test/examples/naming001/E.java
new file mode 100644
index 0000000..b445323
--- /dev/null
+++ b/src/test/examples/naming001/E.java
@@ -0,0 +1,11 @@
+// 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.
+package naming001;
+
+public class E extends D {
+  @Override
+  public void keep() {
+  }
+}
+
diff --git a/src/test/examples/naming001/F.java b/src/test/examples/naming001/F.java
new file mode 100644
index 0000000..3ca60f3
--- /dev/null
+++ b/src/test/examples/naming001/F.java
@@ -0,0 +1,11 @@
+// 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.
+package naming001;
+
+public class F extends E {
+  @Override
+  public void keep() {
+  }
+}
+
diff --git a/src/test/examples/naming001/G.java b/src/test/examples/naming001/G.java
new file mode 100644
index 0000000..175ae8c
--- /dev/null
+++ b/src/test/examples/naming001/G.java
@@ -0,0 +1,16 @@
+// 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.
+package naming001;
+
+public class G implements H {
+  @Override
+  public void m() {
+  }
+
+  public static void main(String[] args) {
+    H i = new G();
+    i.m();
+  }
+}
+
diff --git a/src/test/examples/naming001/H.java b/src/test/examples/naming001/H.java
new file mode 100644
index 0000000..5a82977
--- /dev/null
+++ b/src/test/examples/naming001/H.java
@@ -0,0 +1,8 @@
+// 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.
+package naming001;
+
+public interface H extends I {
+}
+
diff --git a/src/test/examples/naming001/I.java b/src/test/examples/naming001/I.java
new file mode 100644
index 0000000..6ee9ef1
--- /dev/null
+++ b/src/test/examples/naming001/I.java
@@ -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.
+package naming001;
+
+public interface I {
+  void m();
+}
+
diff --git a/src/test/examples/naming001/J.java b/src/test/examples/naming001/J.java
new file mode 100644
index 0000000..f609989
--- /dev/null
+++ b/src/test/examples/naming001/J.java
@@ -0,0 +1,31 @@
+// 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.
+package naming001;
+
+public class J {
+  int[][][] m5(int a, boolean b, long c) {
+    return null;
+  }
+
+  int[][][] m3() {
+    return null;
+  }
+
+  int[][] m2() {
+    return null;
+  }
+
+  int[] m1() {
+    return null;
+  }
+
+  int m0() {
+    return 0;
+  }
+
+  int[][][] m() {
+    return null;
+  }
+}
+
diff --git a/src/test/examples/naming001/K.java b/src/test/examples/naming001/K.java
new file mode 100644
index 0000000..4998773
--- /dev/null
+++ b/src/test/examples/naming001/K.java
@@ -0,0 +1,29 @@
+// 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.
+package naming001;
+
+public class K {
+  private int i;
+  private int h;
+  private final int i2 = 7;
+  private static int j;
+  private final static int i3 = 7;
+
+  private static final Object o = "TAG";
+  private static final String TAG = "TAG";
+  private final String TAG2 = "TAG";
+
+  static {
+    j = 6;
+  }
+
+  {
+    i = 6;
+  }
+
+  void keep() {
+    h = 7;
+  }
+}
+
diff --git a/src/test/examples/naming001/L.java b/src/test/examples/naming001/L.java
new file mode 100644
index 0000000..7e8b03a
--- /dev/null
+++ b/src/test/examples/naming001/L.java
@@ -0,0 +1,13 @@
+// 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.
+package naming001;
+
+public class L {
+  private static final String TAG = "TAG";
+
+  void onReceive() {
+    System.out.println(TAG);
+  }
+}
+
diff --git a/src/test/examples/naming001/Reflect.java b/src/test/examples/naming001/Reflect.java
new file mode 100644
index 0000000..15b920e
--- /dev/null
+++ b/src/test/examples/naming001/Reflect.java
@@ -0,0 +1,53 @@
+// 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.
+package naming001;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+public class Reflect {
+  void keep() throws ClassNotFoundException {
+    Class.forName("naming001.Reflect2");
+    Class.forName("ClassThatDoesNotExists");
+  }
+
+  void keep2() throws NoSuchFieldException, SecurityException {
+    Reflect2.class.getField("fieldPublic");
+    Reflect2.class.getField("fieldPrivate");
+  }
+
+  void keep3() throws NoSuchFieldException, SecurityException {
+    Reflect2.class.getDeclaredField("fieldPublic");
+    Reflect2.class.getDeclaredField("fieldPrivate");
+  }
+
+  void keep4() throws SecurityException, NoSuchMethodException {
+    Reflect2.class.getMethod("m", new Class[] {naming001.Reflect2.A.class});
+    Reflect2.class.getMethod("m", new Class[] {naming001.Reflect2.B.class});
+    Reflect2.class.getMethod("methodThatDoesNotExist",
+        new Class[] {naming001.Reflect2.A.class});
+  }
+
+  void keep5() throws SecurityException, NoSuchMethodException {
+    Reflect2.class.getDeclaredMethod("m", new Class[] {naming001.Reflect2.A.class});
+    Reflect2.class.getDeclaredMethod("m", new Class[] {naming001.Reflect2.B.class});
+  }
+
+  void keep6() throws SecurityException {
+    AtomicIntegerFieldUpdater.newUpdater(Reflect2.class, "fieldPublic");
+  }
+
+  void keep7() throws SecurityException {
+    AtomicLongFieldUpdater.newUpdater(Reflect2.class, "fieldLong");
+    AtomicLongFieldUpdater.newUpdater(Reflect2.class, "fieldLong2");
+  }
+
+  void keep8() throws SecurityException {
+    AtomicReferenceFieldUpdater.newUpdater(Reflect2.class, Reflect2.A.class, "a");
+    AtomicReferenceFieldUpdater.newUpdater(Reflect2.class, Reflect2.A.class, "b");
+    AtomicReferenceFieldUpdater.newUpdater(Reflect2.class, Object.class, "c");
+  }
+}
+
diff --git a/src/test/examples/naming001/Reflect2.java b/src/test/examples/naming001/Reflect2.java
new file mode 100644
index 0000000..42b3075
--- /dev/null
+++ b/src/test/examples/naming001/Reflect2.java
@@ -0,0 +1,40 @@
+// 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.
+package naming001;
+
+public class Reflect2 {
+  public volatile int fieldPublic;
+
+  private volatile int fieldPrivate;
+
+  public volatile long fieldLong;
+
+  private volatile long fieldLong2;
+
+  volatile long fieldLong3;
+
+  protected volatile long fieldLong4;
+
+  public volatile A a;
+
+  public volatile B b;
+
+  private volatile Object c;
+
+  private void calledMethod() {
+  }
+
+  public void m(A a) {
+  }
+
+  private void privateMethod(B b) {
+  }
+
+  class A {
+  }
+
+  class B {
+  }
+}
+
diff --git a/src/test/examples/naming001/keep-rules-001.txt b/src/test/examples/naming001/keep-rules-001.txt
new file mode 100644
index 0000000..72be5a2
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-001.txt
@@ -0,0 +1,7 @@
+# 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.
+
+-allowaccessmodification
+
+-keep class naming001.A
diff --git a/src/test/examples/naming001/keep-rules-002.txt b/src/test/examples/naming001/keep-rules-002.txt
new file mode 100644
index 0000000..1554e31
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-002.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.
+
+-allowaccessmodification
+
+-keep !final class naming001.A {
+  private <methods>;
+}
diff --git a/src/test/examples/naming001/keep-rules-003.txt b/src/test/examples/naming001/keep-rules-003.txt
new file mode 100644
index 0000000..cdcab79
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-003.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.
+
+-allowaccessmodification
+
+-keep !final class naming001.A {
+  !private <methods>;
+}
diff --git a/src/test/examples/naming001/keep-rules-005.txt b/src/test/examples/naming001/keep-rules-005.txt
new file mode 100644
index 0000000..ef4b5c8
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-005.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.
+
+-allowaccessmodification
+
+-keep class naming001.D {
+  public static void main(...);
+}
diff --git a/src/test/examples/naming001/keep-rules-006.txt b/src/test/examples/naming001/keep-rules-006.txt
new file mode 100644
index 0000000..7a8e99e
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-006.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.
+
+-allowaccessmodification
+
+-keep class naming001.G {
+  public static void main(...);
+}
diff --git a/src/test/examples/naming001/keep-rules-014.txt b/src/test/examples/naming001/keep-rules-014.txt
new file mode 100644
index 0000000..6423782
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-014.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.
+
+-allowaccessmodification
+
+-keep class naming001.Reflect {
+  *** keep6();
+}
diff --git a/src/test/examples/naming001/keep-rules-017.txt b/src/test/examples/naming001/keep-rules-017.txt
new file mode 100644
index 0000000..b146df8
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-017.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.
+
+-allowaccessmodification
+
+-keep class naming001.K {
+  void keep();
+}
diff --git a/src/test/examples/naming044/A.java b/src/test/examples/naming044/A.java
new file mode 100644
index 0000000..799f765
--- /dev/null
+++ b/src/test/examples/naming044/A.java
@@ -0,0 +1,8 @@
+// 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.
+package naming044;
+
+public class A {
+  static int f = 8;
+}
diff --git a/src/test/examples/naming044/B.java b/src/test/examples/naming044/B.java
new file mode 100644
index 0000000..7580945
--- /dev/null
+++ b/src/test/examples/naming044/B.java
@@ -0,0 +1,10 @@
+// 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.
+package naming044;
+
+public class B {
+  public static int m() {
+    return A.f;
+  }
+}
diff --git a/src/test/examples/naming044/keep-rules-001.txt b/src/test/examples/naming044/keep-rules-001.txt
new file mode 100644
index 0000000..a191b54
--- /dev/null
+++ b/src/test/examples/naming044/keep-rules-001.txt
@@ -0,0 +1,11 @@
+# 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.
+
+-allowaccessmodification
+
+-repackageclasses ''
+
+-keep,allowobfuscation class * {
+  *;
+}
diff --git a/src/test/examples/naming044/keep-rules-002.txt b/src/test/examples/naming044/keep-rules-002.txt
new file mode 100644
index 0000000..c6e495b
--- /dev/null
+++ b/src/test/examples/naming044/keep-rules-002.txt
@@ -0,0 +1,11 @@
+# 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.
+
+-allowaccessmodification
+
+-repackageclasses 'p44.x'
+
+-keep,allowobfuscation class * {
+  *;
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 1683557..e4b37f2 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -11,10 +11,17 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.shaking.RootSetBuilder;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -421,6 +428,17 @@
         .read();
   }
 
+  public static ProguardConfiguration loadProguardConfiguration(
+      DexItemFactory factory, List<Path> configPaths)
+      throws IOException, ProguardRuleParserException {
+    if (configPaths.isEmpty()) {
+      return ProguardConfiguration.defaultConfiguration(factory);
+    }
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
+    parser.parse(configPaths);
+    return parser.getConfig();
+  }
+
   public static R8Command.Builder prepareR8CommandBuilder(AndroidApp app) {
     return R8Command.builder(app);
   }
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierTest.java b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
new file mode 100644
index 0000000..1124ceb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
@@ -0,0 +1,200 @@
+// 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.
+package com.android.tools.r8.naming;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+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 MinifierTest extends NamingTestBase {
+
+  public MinifierTest(
+      String test,
+      List<String> keepRulesFiles,
+      BiConsumer<DexItemFactory, NamingLens> inspection) {
+    super(test, keepRulesFiles, inspection, new Timing("MinifierTest"));
+  }
+
+  @Test
+  public void minifierTest() throws Exception {
+    NamingLens naming = runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
+    inspection.accept(dexItemFactory, naming);
+  }
+
+  @Parameters(name = "test: {0} keep: {1}")
+  public static Collection<Object[]> data() {
+    List<String> tests = Arrays.asList("naming001");
+
+    Map<String, BiConsumer<DexItemFactory, NamingLens>> inspections = new HashMap<>();
+    inspections.put("naming001:keep-rules-001.txt", MinifierTest::test001_rule001);
+    inspections.put("naming001:keep-rules-002.txt", MinifierTest::test001_rule002);
+    inspections.put("naming001:keep-rules-003.txt", MinifierTest::test001_rule003);
+    inspections.put("naming001:keep-rules-005.txt", MinifierTest::test001_rule005);
+    inspections.put("naming001:keep-rules-006.txt", MinifierTest::test001_rule006);
+    inspections.put("naming001:keep-rules-014.txt", MinifierTest::test001_rule014);
+    inspections.put("naming001:keep-rules-017.txt", MinifierTest::test001_rule017);
+
+    return createTests(tests, inspections);
+  }
+
+  private static void test001_rule001(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType a = dexItemFactory.createType("Lnaming001/A;");
+    // class naming001.A should be kept, according to the keep rule.
+    assertEquals("Lnaming001/A;", naming.lookupDescriptor(a).toSourceString());
+
+    DexMethod m = dexItemFactory.createMethod(
+        a, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+    // method naming001.A.m would be renamed.
+    assertNotEquals("m", naming.lookupName(m).toSourceString());
+
+    DexMethod p = dexItemFactory.createMethod(
+        a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
+    // method naming001.A.privateFunc would be renamed.
+    assertNotEquals("privateFunc", naming.lookupName(p).toSourceString());
+
+    DexType k = dexItemFactory.createType("Lnaming001/K;");
+    DexField h = dexItemFactory.createField(k, dexItemFactory.intType, "h");
+    // field naming001.K.h is dead, not renamed; hence returned as same via identityLens.
+    assertEquals("h", naming.lookupName(h).toSourceString());
+  }
+
+  private static void test001_rule002(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType a = dexItemFactory.createType("Lnaming001/A;");
+    // class naming001.A should be kept, according to the keep rule.
+    assertEquals("Lnaming001/A;", naming.lookupDescriptor(a).toSourceString());
+
+    DexMethod m = dexItemFactory.createMethod(
+        a, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+    // method naming001.A.m would be renamed.
+    assertNotEquals("m", naming.lookupName(m).toSourceString());
+
+    DexMethod p = dexItemFactory.createMethod(
+        a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
+    // method naming001.A.privateFunc should be kept, according to the keep rule.
+    assertEquals("privateFunc", naming.lookupName(p).toSourceString());
+  }
+
+  private static void test001_rule003(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType a = dexItemFactory.createType("Lnaming001/A;");
+    // class naming001.A should be kept, according to the keep rule.
+    assertEquals("Lnaming001/A;", naming.lookupDescriptor(a).toSourceString());
+
+    DexMethod m = dexItemFactory.createMethod(
+        a, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+    // method naming001.A.m should be kept, according to the keep rule.
+    assertEquals("m", naming.lookupName(m).toSourceString());
+
+    DexMethod p = dexItemFactory.createMethod(
+        a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
+    // method naming001.A.privateFunc would be renamed.
+    assertNotEquals("privateFunc", naming.lookupName(p).toSourceString());
+  }
+
+  private static void test001_rule005(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType d = dexItemFactory.createType("Lnaming001/D;");
+    // class naming001.D should be kept, according to the keep rule.
+    assertEquals("Lnaming001/D;", naming.lookupDescriptor(d).toSourceString());
+
+    DexMethod main = dexItemFactory.createMethod(
+        d,
+        dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.stringArrayType),
+        "main");
+    // method naming001.D.main should be kept, according to the keep rule.
+    assertEquals("main", naming.lookupName(main).toSourceString());
+
+    DexMethod k = dexItemFactory.createMethod(
+        d, dexItemFactory.createProto(dexItemFactory.voidType), "keep");
+    // method naming001.D.keep would be renamed.
+    assertNotEquals("keep", naming.lookupName(k).toSourceString());
+
+    // Note that naming001.E extends naming001.D.
+    DexType e = dexItemFactory.createType("Lnaming001/E;");
+    DexMethod inherited_k = dexItemFactory.createMethod(
+        e, dexItemFactory.createProto(dexItemFactory.voidType), "keep");
+    // method naming001.E.keep should be renamed to the same name as naming001.D.keep.
+    assertEquals(
+        naming.lookupName(inherited_k).toSourceString(), naming.lookupName(k).toSourceString());
+  }
+
+  private static void test001_rule006(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType g = dexItemFactory.createType("Lnaming001/G;");
+    // class naming001.G should be kept, according to the keep rule.
+    assertEquals("Lnaming001/G;", naming.lookupDescriptor(g).toSourceString());
+
+    DexMethod main = dexItemFactory.createMethod(
+        g,
+        dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.stringArrayType),
+        "main");
+    // method naming001.G.main should be kept, according to the keep rule.
+    assertEquals("main", naming.lookupName(main).toSourceString());
+
+    DexMethod impl_m = dexItemFactory.createMethod(
+        g, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+    // method naming001.G.m would be renamed.
+    assertNotEquals("m", naming.lookupName(impl_m).toSourceString());
+
+    // Note that naming001.G implements H that extends I, where method m is declared.
+    DexType i = dexItemFactory.createType("Lnaming001/I;");
+    DexMethod def_m = dexItemFactory.createMethod(
+        i, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+    // method naming001.I.m should be renamed to the same name as naming001.G.m.
+    assertEquals(
+        naming.lookupName(impl_m).toSourceString(), naming.lookupName(def_m).toSourceString());
+  }
+
+  private static void test001_rule014(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType reflect = dexItemFactory.createType("Lnaming001/Reflect;");
+    // class naming001.Reflect should be kept, according to the keep rule.
+    assertEquals("Lnaming001/Reflect;", naming.lookupDescriptor(reflect).toSourceString());
+
+    DexMethod keep6 = dexItemFactory.createMethod(
+        reflect, dexItemFactory.createProto(dexItemFactory.voidType), "keep6");
+    // method naming001.Reflect.keep6 should be kept, according to the keep rule.
+    assertEquals("keep6", naming.lookupName(keep6).toSourceString());
+
+    DexType reflect2 = dexItemFactory.createType("Lnaming001/Reflect2;");
+    DexField fieldPublic = dexItemFactory.createField(
+        reflect2, dexItemFactory.intType, "fieldPublic");
+    // method naming001.Reflect.keep6 accesses to naming001.Reflect2.fieldPublic via reflection.
+    assertEquals("fieldPublic", naming.lookupName(fieldPublic).toSourceString());
+  }
+
+  private static void test001_rule017(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType k = dexItemFactory.createType("Lnaming001/K;");
+    // class naming001.K should be kept, according to the keep rule.
+    assertEquals("Lnaming001/K;", naming.lookupDescriptor(k).toSourceString());
+
+    DexMethod keep = dexItemFactory.createMethod(
+        k, dexItemFactory.createProto(dexItemFactory.voidType), "keep");
+    // method naming001.K.keep should be kept, according to the keep rule.
+    assertEquals("keep", naming.lookupName(keep).toSourceString());
+
+    DexField i = dexItemFactory.createField(k, dexItemFactory.intType, "i");
+    // field naming001.K.i
+    assertEquals("i", naming.lookupName(i).toSourceString());
+
+    DexField j = dexItemFactory.createField(k, dexItemFactory.intType, "j");
+    // field naming001.K.j
+    assertEquals("j", naming.lookupName(j).toSourceString());
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
new file mode 100644
index 0000000..b0fe113
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -0,0 +1,124 @@
+// 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.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.ClassAndMemberPublicizer;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.shaking.RootSetBuilder;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiConsumer;
+import org.junit.Before;
+
+abstract class NamingTestBase {
+
+  private final String appFileName;
+  final List<String> keepRulesFiles;
+  final BiConsumer<DexItemFactory, NamingLens> inspection;
+
+  private final Timing timing;
+
+  private DexApplication program;
+  DexItemFactory dexItemFactory;
+  private AppInfoWithSubtyping appInfo;
+
+  NamingTestBase(
+      String test,
+      List<String> keepRulesFiles,
+      BiConsumer<DexItemFactory, NamingLens> inspection,
+      Timing timing) {
+    appFileName = ToolHelper.EXAMPLES_BUILD_DIR + test + "/classes.dex";
+    this.keepRulesFiles = keepRulesFiles;
+    this.inspection = inspection;
+    this.timing = timing;
+  }
+
+  @Before
+  public void readApp() throws IOException, ExecutionException, ProguardRuleParserException {
+    program = ToolHelper.buildApplication(ImmutableList.of(appFileName));
+    dexItemFactory = program.dexItemFactory;
+    appInfo = new AppInfoWithSubtyping(program);
+  }
+
+  NamingLens runMinifier(List<Path> configPaths)
+      throws IOException, ProguardRuleParserException, ExecutionException {
+    ProguardConfiguration configuration =
+        ToolHelper.loadProguardConfiguration(dexItemFactory, configPaths);
+    InternalOptions options = new InternalOptions();
+    copyProguardConfigurationToInternalOptions(configuration, options);
+
+    if (options.allowAccessModification) {
+      ClassAndMemberPublicizer.run(program);
+    }
+
+    RootSet rootSet = new RootSetBuilder(program, appInfo, configuration.getRules())
+        .run(ThreadUtils.getExecutorService(options));
+    Enqueuer enqueuer = new Enqueuer(appInfo);
+    appInfo = enqueuer.traceApplication(rootSet, timing);
+    return new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
+  }
+
+  private void copyProguardConfigurationToInternalOptions(
+      ProguardConfiguration config, InternalOptions options) {
+    options.packagePrefix = config.getPackagePrefix();
+    options.allowAccessModification = config.getAllowAccessModification();
+    options.classObfuscationDictionary = config.getClassObfuscationDictionary();
+    options.obfuscationDictionary = config.getObfuscationDictionary();
+    options.keepRules = config.getRules();
+  }
+
+  static <T> Collection<Object[]> createTests(List<String> tests, Map<String, T> inspections) {
+    List<Object[]> testCases = new ArrayList<>();
+    Set<String> usedInspections = new HashSet<>();
+    for (String test : tests) {
+      File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + test)
+          .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
+      for (File keepFile : keepFiles) {
+        String keepName = keepFile.getName();
+        T inspection = getTestOptionalParameter(inspections, usedInspections, test, keepName);
+        if (inspection != null) {
+          testCases.add(new Object[]{test, ImmutableList.of(keepFile.getPath()), inspection});
+        }
+      }
+    }
+    assert usedInspections.size() == inspections.size();
+    return testCases;
+  }
+
+  private static <T> T getTestOptionalParameter(
+      Map<String, T> specifications,
+      Set<String> usedSpecifications,
+      String test,
+      String keepName) {
+    T parameter = specifications.get(test);
+    if (parameter == null) {
+      parameter = specifications.get(test + ":" + keepName);
+      if (parameter != null) {
+        usedSpecifications.add(test + ":" + keepName);
+      }
+    } else {
+      usedSpecifications.add(test);
+    }
+    return parameter;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
new file mode 100644
index 0000000..3e9b269
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -0,0 +1,72 @@
+// 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.
+package com.android.tools.r8.naming;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+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 PackageNamingTest extends NamingTestBase {
+
+  public PackageNamingTest(
+      String test,
+      List<String> keepRulesFiles,
+      BiConsumer<DexItemFactory, NamingLens> inspection) {
+    super(test, keepRulesFiles, inspection, new Timing("PackageNamingTest"));
+  }
+
+  @Test
+  public void packageNamingTest() throws Exception {
+    NamingLens naming = runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
+    inspection.accept(dexItemFactory, naming);
+  }
+
+  @Parameters(name = "test: {0} keep: {1}")
+  public static Collection<Object[]> data() {
+    List<String> tests = Arrays.asList("naming044");
+
+    Map<String, BiConsumer<DexItemFactory, NamingLens>> inspections = new HashMap<>();
+    inspections.put("naming044:keep-rules-001.txt", PackageNamingTest::test044_rule001);
+    inspections.put("naming044:keep-rules-002.txt", PackageNamingTest::test044_rule002);
+
+    return createTests(tests, inspections);
+  }
+
+  private static void test044_rule001(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType b = dexItemFactory.createType("Lnaming044/B;");
+    // All classes are moved to the top-level package, hence no package separator.
+    assertFalse(naming.lookupDescriptor(b).toSourceString().contains("/"));
+
+    DexMethod m = dexItemFactory.createMethod(
+        b, dexItemFactory.createProto(dexItemFactory.intType), "m");
+    // method naming044.B.m would be renamed.
+    assertNotEquals("m", naming.lookupName(m).toSourceString());
+  }
+
+  private static void test044_rule002(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType a = dexItemFactory.createType("Lnaming044/A;");
+    assertTrue(naming.lookupDescriptor(a).toSourceString().startsWith("Lp44/x/"));
+
+    DexType b = dexItemFactory.createType("Lnaming044/B;");
+    assertTrue(naming.lookupDescriptor(b).toSourceString().startsWith("Lp44/x/"));
+  }
+}