Version 2.0.72

Cherry pick: Add -dontusemixedcase test showing that we can pick
clashing names
CL: https://r8-review.googlesource.com/50822

Cherry pick: Naming of packages can collide if -dontusemixedcase
CL: https://r8-review.googlesource.com/50825

Cherry pick: Track lower case type names when using -dontusemixedcase
CL: https://r8-review.googlesource.com/50827

Cherry pick: Update ServiceLoaderRewriter to synchronize on synthetic
clazz creation
CL: https://r8-review.googlesource.com/50340

Bug: 155240931
Bug: 155249069
Bug: 154360339
Bug: 153127674
Bug: 155106999
Change-Id: I6af6169e8684ad511001f306659817e01e6e556f
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 0bb2d98..e509746 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "2.0.71";
+  public static final String LABEL = "2.0.72";
 
   private Version() {
   }
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 ab96c0d..c7934e7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph;
 
 import static com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement.computeLeastUpperBoundOfInterfaces;
+import static com.android.tools.r8.ir.optimize.ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME;
 import static com.google.common.base.Predicates.alwaysTrue;
 
 import com.android.tools.r8.dex.Constants;
@@ -319,6 +320,8 @@
   public final DexType invocationHandlerType = createType(invocationHandlerDescriptor);
   public final DexType proxyType = createType(proxyDescriptor);
   public final DexType serviceLoaderType = createType(serviceLoaderDescriptor);
+  public final DexType serviceLoaderRewrittenClassType =
+      createType("L" + SERVICE_LOADER_CLASS_NAME + ";");
   public final DexType serviceLoaderConfigurationErrorType =
       createType(serviceLoaderConfigurationErrorDescriptor);
   public final DexType listType = createType(listDescriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index e8799da..4f5d248 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -36,6 +36,7 @@
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * ServiceLoaderRewriter will attempt to rewrite calls on the form of: ServiceLoader.load(X.class,
@@ -69,7 +70,7 @@
   public static final String SERVICE_LOADER_CLASS_NAME = "$$ServiceLoaderMethods";
   private static final String SERVICE_LOADER_METHOD_PREFIX_NAME = "$load";
 
-  private DexProgramClass synthesizedClass;
+  private AtomicReference<DexProgramClass> synthesizedClass = new AtomicReference<>();
   private ConcurrentHashMap<DexType, DexEncodedMethod> synthesizedServiceLoaders =
       new ConcurrentHashMap<>();
 
@@ -82,7 +83,7 @@
   }
 
   public DexProgramClass getSynthesizedClass() {
-    return synthesizedClass;
+    return synthesizedClass.get();
   }
 
   public void rewrite(IRCode code) {
@@ -184,40 +185,12 @@
   }
 
   private DexEncodedMethod createSynthesizedMethod(DexType serviceType, List<DexClass> classes) {
-    DexType serviceLoaderType =
-        appView.dexItemFactory().createType("L" + SERVICE_LOADER_CLASS_NAME + ";");
-    if (synthesizedClass == null) {
-      assert !appView.options().encodeChecksums;
-      ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
-      synthesizedClass =
-          new DexProgramClass(
-              serviceLoaderType,
-              null,
-              new SynthesizedOrigin("Service Loader desugaring", getClass()),
-              ClassAccessFlags.fromDexAccessFlags(
-                  Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
-              appView.dexItemFactory().objectType,
-              DexTypeList.empty(),
-              appView.dexItemFactory().createString("ServiceLoader"),
-              null,
-              Collections.emptyList(),
-              null,
-              Collections.emptyList(),
-              DexAnnotationSet.empty(),
-              DexEncodedField.EMPTY_ARRAY, // Static fields.
-              DexEncodedField.EMPTY_ARRAY, // Instance fields.
-              DexEncodedMethod.EMPTY_ARRAY,
-              DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
-              appView.dexItemFactory().getSkipNameValidationForTesting(),
-              checksumSupplier);
-      appView.appInfo().addSynthesizedClass(synthesizedClass);
-    }
     DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
     DexMethod method =
         appView
             .dexItemFactory()
             .createMethod(
-                serviceLoaderType,
+                appView.dexItemFactory().serviceLoaderRewrittenClassType,
                 proto,
                 SERVICE_LOADER_METHOD_PREFIX_NAME + atomicInteger.incrementAndGet());
     MethodAccessFlags methodAccess =
@@ -230,10 +203,48 @@
             ParameterAnnotationsList.empty(),
             ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()),
             true);
-    synthesizedClass.addDirectMethod(encodedMethod);
+    getOrSetSynthesizedClass().addDirectMethod(encodedMethod);
     return encodedMethod;
   }
 
+  private DexProgramClass getOrSetSynthesizedClass() {
+    if (synthesizedClass.get() != null) {
+      return synthesizedClass.get();
+    }
+    assert !appView.options().encodeChecksums;
+    ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
+    DexProgramClass clazz =
+        synthesizedClass.updateAndGet(
+            existingClazz -> {
+              if (existingClazz != null) {
+                return existingClazz;
+              }
+              return new DexProgramClass(
+                  appView.dexItemFactory().serviceLoaderRewrittenClassType,
+                  null,
+                  new SynthesizedOrigin("Service Loader desugaring", getClass()),
+                  ClassAccessFlags.fromDexAccessFlags(
+                      Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
+                  appView.dexItemFactory().objectType,
+                  DexTypeList.empty(),
+                  appView.dexItemFactory().createString("ServiceLoader"),
+                  null,
+                  Collections.emptyList(),
+                  null,
+                  Collections.emptyList(),
+                  DexAnnotationSet.empty(),
+                  DexEncodedField.EMPTY_ARRAY, // Static fields.
+                  DexEncodedField.EMPTY_ARRAY, // Instance fields.
+                  DexEncodedMethod.EMPTY_ARRAY,
+                  DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+                  appView.dexItemFactory().getSkipNameValidationForTesting(),
+                  checksumSupplier);
+            });
+    assert clazz != null;
+    appView.appInfo().addSynthesizedClass(clazz);
+    return clazz;
+  }
+
   /**
    * Rewriter assumes that the code is of the form:
    *
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 6e04d4a..512dde8 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -48,13 +48,14 @@
   private final boolean isAccessModificationAllowed;
   private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
   private final Set<String> usedPackagePrefixes = Sets.newHashSet();
-  private final Set<DexString> usedTypeNames = Sets.newIdentityHashSet();
-
+  private final Set<String> usedTypeNames = Sets.newHashSet();
   private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
   private final Map<String, Namespace> states = new HashMap<>();
   private final boolean keepInnerClassStructure;
 
   private final Namespace topLevelState;
+  private final boolean allowMixedCaseNaming;
+  private final Predicate<String> isUsed;
 
   ClassNameMinifier(
       AppView<AppInfoWithLiveness> appView,
@@ -76,11 +77,23 @@
         getPackageBinaryNameFromJavaType(options.getProguardConfiguration().getPackagePrefix()));
 
     states.put("", topLevelState);
+
+    if (options.getProguardConfiguration().hasDontUseMixedCaseClassnames()) {
+      allowMixedCaseNaming = false;
+      isUsed = candidate -> usedTypeNames.contains(candidate.toLowerCase());
+    } else {
+      allowMixedCaseNaming = true;
+      isUsed = usedTypeNames::contains;
+    }
+  }
+
+  private void setUsedTypeName(String typeName) {
+    usedTypeNames.add(allowMixedCaseNaming ? typeName : typeName.toLowerCase());
   }
 
   static class ClassRenaming {
-    protected final Map<String, String> packageRenaming;
-    protected final Map<DexType, DexString> classRenaming;
+    final Map<String, String> packageRenaming;
+    final Map<DexType, DexString> classRenaming;
 
     private ClassRenaming(
         Map<DexType, DexString> classRenaming, Map<String, String> packageRenaming) {
@@ -195,7 +208,7 @@
     renaming.put(type, descriptor);
     registerPackagePrefixesAsUsed(
         getParentPackagePrefix(getClassBinaryNameFromDescriptor(descriptor.toSourceString())));
-    usedTypeNames.add(descriptor);
+    setUsedTypeName(descriptor.toString());
     if (keepInnerClassStructure) {
       DexType outerClass = getOutClassForType(type);
       if (outerClass != null) {
@@ -387,10 +400,9 @@
     }
 
     DexString nextTypeName(DexType type) {
-      DexString candidate =
-          classNamingStrategy.next(type, packagePrefix, this, usedTypeNames::contains);
-      assert !usedTypeNames.contains(candidate);
-      usedTypeNames.add(candidate);
+      DexString candidate = classNamingStrategy.next(type, packagePrefix, this, isUsed);
+      assert !usedTypeNames.contains(candidate.toString());
+      setUsedTypeName(candidate.toString());
       return candidate;
     }
 
@@ -420,7 +432,7 @@
 
   protected interface ClassNamingStrategy {
     DexString next(
-        DexType type, char[] packagePrefix, InternalNamingState state, Predicate<DexString> isUsed);
+        DexType type, char[] packagePrefix, InternalNamingState state, Predicate<String> isUsed);
 
     /**
      * Returns the reserved descriptor for a type. If the type is not allowed to be obfuscated
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 185554c..4407fdc 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -148,11 +148,8 @@
 
     @Override
     public DexString next(
-        DexType type,
-        char[] packagePrefix,
-        InternalNamingState state,
-        Predicate<DexString> isUsed) {
-      DexString candidate = null;
+        DexType type, char[] packagePrefix, InternalNamingState state, Predicate<String> isUsed) {
+      String candidate = null;
       String lastName = null;
       do {
         String newName = nextName(packagePrefix, state, false) + ";";
@@ -170,9 +167,9 @@
         if (newName.endsWith("LR;") || newName.endsWith("/R;")) {
           continue;
         }
-        candidate = factory.createString(newName);
+        candidate = newName;
       } while (candidate == null || isUsed.test(candidate));
-      return candidate;
+      return factory.createString(candidate);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 09aaea5..659666c 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -403,10 +403,7 @@
 
     @Override
     public DexString next(
-        DexType type,
-        char[] packagePrefix,
-        InternalNamingState state,
-        Predicate<DexString> isUsed) {
+        DexType type, char[] packagePrefix, InternalNamingState state, Predicate<String> isUsed) {
       assert !mappings.containsKey(type);
       assert appView.rootSet().mayBeMinified(type, appView);
       return super.next(
@@ -414,7 +411,7 @@
           packagePrefix,
           state,
           candidate -> {
-            if (mappedNames.contains(candidate.toString())) {
+            if (mappedNames.contains(candidate)) {
               return true;
             }
             return isUsed.test(candidate);
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 27af1aa..e9ac10d 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -12,6 +12,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
 
 public abstract class TestBuilder<RR extends TestRunResult, T extends TestBuilder<RR, T>> {
 
@@ -38,6 +39,14 @@
     return self();
   }
 
+  public T ifTrue(boolean value, Consumer<T> consumer) {
+    T self = self();
+    if (value) {
+      consumer.accept(self);
+    }
+    return self;
+  }
+
   @Deprecated
   public abstract RR run(String mainClass)
       throws CompilationFailedException, ExecutionException, IOException;
diff --git a/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java b/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
index 4f3a868..e0ef67d 100644
--- a/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
@@ -40,7 +40,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public ClassObfuscationDictionaryDuplicateTest(TestParameters parameters) {
@@ -61,8 +61,7 @@
         .noTreeShaking()
         .addKeepRules("-classobfuscationdictionary " + dictionary.toString())
         .addKeepMainRule(C.class)
-        .setMinApi(parameters.getRuntime())
-        .compile()
+        .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), C.class)
         .assertSuccessWithOutput("HELLO WORLD!")
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java b/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
new file mode 100644
index 0000000..b16347d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, 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.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 DontUseMixedCaseClassNamesExistingClassTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final boolean dontUseMixedCase;
+  private static final String EXPECTED = "A.foo";
+  private static final String FINAL_CLASS_NAME = "DontUseMixedCaseClassNamesExistingClassTest$main";
+
+  @Parameters(name = "{0}, dontusemixedcaseclassnames: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  public DontUseMixedCaseClassNamesExistingClassTest(
+      TestParameters parameters, boolean dontUseMixedCase) {
+    this.parameters = parameters;
+    this.dontUseMixedCase = dontUseMixedCase;
+  }
+
+  @Test
+  public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+    Path dictionary = temp.getRoot().toPath().resolve("dictionary.txt");
+    FileUtils.writeTextFile(dictionary, FINAL_CLASS_NAME);
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepClassRulesWithAllowObfuscation(A.class)
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-classobfuscationdictionary " + dictionary.toString())
+        .ifTrue(dontUseMixedCase, b -> b.addKeepRules("-dontusemixedcaseclassnames"))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED)
+        .inspect(
+            inspector -> {
+              String finalName = Main.class.getPackage().getName() + "." + FINAL_CLASS_NAME;
+              assertEquals(finalName.toLowerCase(), Main.class.getTypeName().toLowerCase());
+              if (dontUseMixedCase) {
+                assertNotEquals(finalName, inspector.clazz(A.class).getFinalName());
+              } else {
+                assertEquals(finalName, inspector.clazz(A.class).getFinalName());
+              }
+            });
+  }
+
+  public static class A { // Will be renamed to main if not -dontusemixedcaseclassnames
+
+    public void foo() {
+      System.out.println("A.foo");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new A().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java b/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
index f10947e..133e607 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.naming.keeppackagenames.Top;
-import com.android.tools.r8.naming.packageobfucationdict.A;
+import com.android.tools.r8.naming.packageobfuscationdict.A;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/naming/b155249069/A/A.java b/src/test/java/com/android/tools/r8/naming/b155249069/A/A.java
new file mode 100644
index 0000000..c0690b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b155249069/A/A.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.b155249069.A;
+
+public class A {
+
+  public void foo() {
+    System.out.println("A.A.foo()");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java b/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
new file mode 100644
index 0000000..a55ef4b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, 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.b155249069;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.b155249069.package_b.A;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 DontUseMixedCaseClassNamesExistingClassPackageTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final boolean dontUseMixedCase;
+
+  @Parameters(name = "{0}, dontusemixedcaseclassnames: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  public DontUseMixedCaseClassNamesExistingClassPackageTest(
+      TestParameters parameters, boolean dontUseMixedCase) {
+    this.parameters = parameters;
+    this.dontUseMixedCase = dontUseMixedCase;
+  }
+
+  @Test
+  public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+    Path packageDictionary = temp.getRoot().toPath().resolve("packagedictionary.txt");
+    // Suggest the name 'a' for the package, to force a collision with A.A.
+    FileUtils.writeTextFile(packageDictionary, "a");
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class, com.android.tools.r8.naming.b155249069.A.A.class, A.class)
+        .setMinApi(parameters.getApiLevel())
+        // Keep A.A such that the package A is kept.
+        .addKeepClassRules(com.android.tools.r8.naming.b155249069.A.A.class)
+        .addKeepClassRulesWithAllowObfuscation(A.class)
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-packageobfuscationdictionary " + packageDictionary.toString())
+        .ifTrue(dontUseMixedCase, b -> b.addKeepRules("-dontusemixedcaseclassnames"))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A.A.foo()", "package_b.B.foo()")
+        .inspect(
+            inspector -> {
+              ClassSubject aSubject =
+                  inspector.clazz(com.android.tools.r8.naming.b155249069.A.A.class);
+              ClassSubject bSubject = inspector.clazz(A.class);
+              if (dontUseMixedCase) {
+                assertNotEquals(
+                    aSubject.getFinalName().toLowerCase(), bSubject.getFinalName().toLowerCase());
+              } else {
+                assertEquals(
+                    aSubject.getFinalName().toLowerCase(), bSubject.getFinalName().toLowerCase());
+              }
+            });
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new com.android.tools.r8.naming.b155249069.A.A().foo();
+      new A().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b155249069/package_b/A.java b/src/test/java/com/android/tools/r8/naming/b155249069/package_b/A.java
new file mode 100644
index 0000000..a02a90d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b155249069/package_b/A.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.b155249069.package_b;
+
+public class A {
+
+  public void foo() {
+    System.out.println("package_b.B.foo()");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/packageobfucationdict/A.java b/src/test/java/com/android/tools/r8/naming/packageobfuscationdict/A.java
similarity index 79%
rename from src/test/java/com/android/tools/r8/naming/packageobfucationdict/A.java
rename to src/test/java/com/android/tools/r8/naming/packageobfuscationdict/A.java
index 012140e..67f256b 100644
--- a/src/test/java/com/android/tools/r8/naming/packageobfucationdict/A.java
+++ b/src/test/java/com/android/tools/r8/naming/packageobfuscationdict/A.java
@@ -2,6 +2,6 @@
 // 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.packageobfucationdict;
+package com.android.tools.r8.naming.packageobfuscationdict;
 
 public class A {}