Version 1.4.71

Don't use qualified entries in apply-mapping.
Fix and regressions tests added directly to 1.4, to be picked to master.

Bug: 121042934
Change-Id: I610b13ad5ac46c70a5a9624db80fd9ccc1c1f79c
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 30d9b3b..2f7ec60 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 = "1.4.70";
+  public static final String LABEL = "1.4.71";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 094dcaf..c76b422 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -106,6 +106,8 @@
 
     abstract void write(Writer builder) throws IOException;
 
+    boolean isQualified() { return name.contains("."); }
+
     @Override
     public String toString() {
       try {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
index d95f299..a447e37 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
@@ -191,7 +191,7 @@
       // e.g., private methods with same names, could be mapped to a wrong renamed name.
       classNaming.forAllFieldNaming(memberNaming -> {
         FieldSignature signature = (FieldSignature) memberNaming.getOriginalSignature();
-        if (!appliedMemberSignature.contains(signature)) {
+        if (!signature.isQualified() && !appliedMemberSignature.contains(signature)) {
           DexField pretendedOriginalField = signature.toDexField(appInfo.dexItemFactory, from);
           if (appInfo.definitionFor(pretendedOriginalField) == null) {
             applyFieldMapping(pretendedOriginalField, memberNaming);
@@ -200,7 +200,7 @@
       });
       classNaming.forAllMethodNaming(memberNaming -> {
         MethodSignature signature = (MethodSignature) memberNaming.getOriginalSignature();
-        if (!appliedMemberSignature.contains(signature)) {
+        if (!signature.isQualified() && !appliedMemberSignature.contains(signature)) {
           DexMethod pretendedOriginalMethod = signature.toDexMethod(appInfo.dexItemFactory, from);
           if (appInfo.definitionFor(pretendedOriginalMethod) == null) {
             applyMethodMapping(pretendedOriginalMethod, memberNaming);
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index e8555d7..7f06ea2 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -12,11 +12,14 @@
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -45,6 +48,7 @@
   private boolean enableUnusedArgumentAnnotations = false;
   private CollectingGraphConsumer graphConsumer = null;
   private List<String> keepRules = new ArrayList<>();
+  private List<String> applyMappingMaps = new ArrayList<>();
 
   @Override
   R8TestBuilder self() {
@@ -70,6 +74,21 @@
     builder.setDisableMinification(!enableMinification);
     builder.setProguardMapConsumer((string, ignore) -> proguardMapBuilder.append(string));
 
+    if (!applyMappingMaps.isEmpty()) {
+      try {
+        Path mappingsDir = getState().getNewTempFolder();
+        for (int i = 0; i < applyMappingMaps.size(); i++) {
+          String mapContent = applyMappingMaps.get(i);
+          Path mapPath = mappingsDir.resolve("mapping" + i + ".map");
+          FileUtils.writeTextFile(mapPath, mapContent);
+          builder.addProguardConfiguration(
+              Collections.singletonList("-applymapping " + mapPath.toString()), Origin.unknown());
+        }
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
     class Box {
       private List<ProguardConfigurationRule> syntheticProguardRules;
       private ProguardConfiguration proguardConfiguration;
@@ -208,6 +227,11 @@
     return self();
   }
 
+  public R8TestBuilder addApplyMapping(String proguardMap) {
+    applyMappingMaps.add(proguardMap);
+    return self();
+  }
+
   private void addInternalKeepRules(String... rules) {
     // We don't add these to the keep-rule set for other test provided rules.
     builder.addProguardConfiguration(Arrays.asList(rules), Origin.unknown());
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index ecb0a92..95ec572 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -94,4 +94,8 @@
   public R8TestRunResult createRunResult(ProcessResult result) {
     return new R8TestRunResult(app, result, proguardMap, this::graphInspector);
   }
+
+  public String getProguardMap() {
+    return proguardMap;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
index 5fcd1b5..1304357 100644
--- a/src/test/java/com/android/tools/r8/TestBaseBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
@@ -4,9 +4,14 @@
 
 package com.android.tools.r8;
 
+import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableMap;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
 
 public abstract class TestBaseBuilder<
         C extends BaseCommand,
@@ -43,6 +48,35 @@
     return self();
   }
 
+  @Override
+  public T addLibraryClasses(Collection<Class<?>> classes) {
+    builder.addLibraryResourceProvider(
+        new ClassFileResourceProvider() {
+          final Map<String, ProgramResource> resources;
+
+          {
+            ImmutableMap.Builder<String, ProgramResource> builder = ImmutableMap.builder();
+            classes.forEach(
+                c ->
+                    builder.put(
+                        DescriptorUtils.javaTypeToDescriptor(c.getTypeName()),
+                        ProgramResource.fromFile(Kind.CF, ToolHelper.getClassFileForTestClass(c))));
+            resources = builder.build();
+          }
+
+          @Override
+          public Set<String> getClassDescriptors() {
+            return resources.keySet();
+          }
+
+          @Override
+          public ProgramResource getProgramResource(String descriptor) {
+            return resources.get(descriptor);
+          }
+        });
+    return self();
+  }
+
   public T addMainDexListFiles(Collection<Path> files) {
     builder.addMainDexListFiles(files);
     return self();
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index cee2519..30159d3 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.debug.DebugTestConfig;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.utils.ListUtils;
 import java.io.IOException;
 import java.nio.file.Path;
@@ -85,7 +86,7 @@
   }
 
   public T addLibraryClasses(Collection<Class<?>> classes) {
-    return addLibraryFiles(getFilesForClasses(classes));
+    throw new Unimplemented("Unsupported addLibraryClasses");
   }
 
   public T addLibraryFiles(Path... files) {
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index a17ab09..cbf8e81 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -82,6 +82,13 @@
     return self();
   }
 
+  public T addKeepClassAndDefaultConstructor(Class<?>... classes) {
+    for (Class<?> clazz : classes) {
+      addKeepRules("-keep class " + clazz.getTypeName() + " { <init>(); }");
+    }
+    return self();
+  }
+
   public T addKeepPackageRules(Package pkg) {
     return addKeepRules("-keep class " + pkg.getName() + ".*");
   }
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
new file mode 100644
index 0000000..bae227f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
@@ -0,0 +1,116 @@
+// 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.naming.applymapping;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Collections;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ApplyMappingAfterHorizontalMergingFieldTest extends TestBase {
+
+  // Will merge with B.
+  public static class LibraryA {
+
+    public static boolean bar;
+  }
+
+  // Will merge with A.
+  public static class LibraryB {
+
+    public static boolean foo;
+  }
+
+  // Ensure kept entry hitting the merged classes.
+  public static class LibraryMain {
+
+    public static void main(String[] args) {
+      LibraryA.bar = System.nanoTime() > 0;
+      LibraryB.foo = args.length < 123;
+      System.out.println(LibraryA.bar && LibraryB.foo);
+    }
+  }
+
+  // Program class simply calling library main.
+  public static class ProgramClass {
+
+    public static void main(String[] args) {
+      LibraryMain.main(args);
+    }
+  }
+
+  // Test runner code follows.
+
+  private static final Class<?>[] LIBRARY_CLASSES = {
+      NeverInline.class,
+      LibraryA.class,
+      LibraryB.class,
+      LibraryMain.class
+  };
+
+  private static final Class<?>[] PROGRAM_CLASSES = {
+      ProgramClass.class
+  };
+
+  private Backend backend;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  public ApplyMappingAfterHorizontalMergingFieldTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test
+  public void runOnJvm() throws Throwable {
+    Assume.assumeTrue(backend == Backend.CF);
+    testForJvm()
+        .addProgramClasses(LIBRARY_CLASSES)
+        .addProgramClasses(PROGRAM_CLASSES)
+        .run(ProgramClass.class)
+        .assertSuccessWithOutput(StringUtils.lines("true"));
+  }
+
+  @Test
+  public void b121042934() throws Exception {
+    R8TestCompileResult libraryResult = testForR8(backend)
+        .enableInliningAnnotations()
+        .addProgramClasses(LIBRARY_CLASSES)
+        .addKeepMainRule(LibraryMain.class)
+        .compile();
+
+    CodeInspector inspector = libraryResult.inspector();
+    assertThat(inspector.clazz(LibraryMain.class), isPresent());
+    // Classes A and B have been merged, check only one remains.
+    assertTrue(inspector.clazz(LibraryA.class).isPresent()
+        != inspector.clazz(LibraryB.class).isPresent());
+
+    Path libraryOut = temp.newFolder().toPath().resolve("out.jar");
+    libraryResult.writeToZip(libraryOut);
+    testForR8(backend)
+        .noTreeShaking()
+        .noMinification()
+        .addProgramClasses(PROGRAM_CLASSES)
+        .addApplyMapping(libraryResult.getProguardMap())
+        .addLibraryClasses(LIBRARY_CLASSES)
+        .compile()
+        .addRunClasspath(Collections.singletonList(libraryOut))
+        .run(ProgramClass.class)
+        .assertSuccessWithOutput(StringUtils.lines("true"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java
new file mode 100644
index 0000000..88cb16a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java
@@ -0,0 +1,120 @@
+// 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.naming.applymapping;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Collections;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ApplyMappingAfterHorizontalMergingMethodTest extends TestBase {
+
+  // Will merge with B.
+  public static class LibraryA {
+
+    @NeverInline
+    public static String bar() {
+      return "LibraryA::foo";
+    }
+  }
+
+  // Will merge with A.
+  public static class LibraryB {
+
+    @NeverInline
+    public static String foo() {
+      return LibraryA.bar();
+    }
+  }
+
+  // Ensure kept entry hitting the merged classes.
+  public static class LibraryMain {
+
+    public static void main(String[] args) {
+      System.out.println(LibraryB.foo());
+    }
+  }
+
+  // Program class simply calling library main.
+  public static class ProgramClass {
+
+    public static void main(String[] args) {
+      LibraryMain.main(args);
+    }
+  }
+
+  // Test runner code follows.
+
+  private static final Class<?>[] LIBRARY_CLASSES = {
+      NeverInline.class,
+      LibraryA.class,
+      LibraryB.class,
+      LibraryMain.class
+  };
+
+  private static final Class<?>[] PROGRAM_CLASSES = {
+      ProgramClass.class
+  };
+
+  private Backend backend;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  public ApplyMappingAfterHorizontalMergingMethodTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test
+  public void runOnJvm() throws Throwable {
+    Assume.assumeTrue(backend == Backend.CF);
+    testForJvm()
+        .addProgramClasses(LIBRARY_CLASSES)
+        .addProgramClasses(PROGRAM_CLASSES)
+        .run(ProgramClass.class)
+        .assertSuccessWithOutput(StringUtils.lines("LibraryA::foo"));
+  }
+
+  @Test
+  public void b121042934() throws Exception {
+    R8TestCompileResult libraryResult = testForR8(backend)
+        .enableInliningAnnotations()
+        .addProgramClasses(LIBRARY_CLASSES)
+        .addKeepMainRule(LibraryMain.class)
+        .compile();
+
+    CodeInspector inspector = libraryResult.inspector();
+    assertThat(inspector.clazz(LibraryMain.class), isPresent());
+    // Classes A and B have been merged, check only one remains.
+    assertTrue(inspector.clazz(LibraryA.class).isPresent()
+        != inspector.clazz(LibraryB.class).isPresent());
+
+    Path libraryOut = temp.newFolder().toPath().resolve("out.jar");
+    libraryResult.writeToZip(libraryOut);
+    testForR8(backend)
+        .noTreeShaking()
+        .noMinification()
+        .addProgramClasses(PROGRAM_CLASSES)
+        .addApplyMapping(libraryResult.getProguardMap())
+        .addLibraryClasses(LIBRARY_CLASSES)
+        .compile()
+        .addRunClasspath(Collections.singletonList(libraryOut))
+        .run(ProgramClass.class)
+        .assertSuccessWithOutput(StringUtils.lines("LibraryA::foo"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingFieldTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingFieldTest.java
new file mode 100644
index 0000000..2a35205
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingFieldTest.java
@@ -0,0 +1,107 @@
+// 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.naming.applymapping;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Collections;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ApplyMappingAfterVerticalMergingFieldTest extends TestBase {
+
+  // Base class will be vertical class merged into subclass
+  public static class LibraryBase {
+
+    public boolean foo = System.nanoTime() > 0;
+  }
+
+  // Subclass targeted via vertical class merging. The main method ensures a reference to foo.
+  public static class LibrarySubclass extends LibraryBase {
+
+    public static void main(String[] args) {
+      System.out.println(new LibrarySubclass().foo);
+    }
+  }
+
+  // Program class that uses LibrarySubclass but the library does not explicitly keep foo and
+  // should thus fail at runtime.
+  public static class ProgramClass extends LibrarySubclass {
+
+    public static void main(String[] args) {
+      System.out.println(new ProgramClass().foo);
+    }
+  }
+
+  // Test runner code follows.
+
+  private static final Class<?>[] LIBRARY_CLASSES = {
+    NeverMerge.class, LibraryBase.class, LibrarySubclass.class
+  };
+
+  private static final Class<?>[] PROGRAM_CLASSES = {
+      ProgramClass.class
+  };
+
+  private Backend backend;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  public ApplyMappingAfterVerticalMergingFieldTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test
+  public void runOnJvm() throws Throwable {
+    Assume.assumeTrue(backend == Backend.CF);
+    testForJvm()
+        .addProgramClasses(LIBRARY_CLASSES)
+        .addProgramClasses(PROGRAM_CLASSES)
+        .run(ProgramClass.class)
+        .assertSuccessWithOutput(StringUtils.lines("true"));
+  }
+
+  @Test
+  public void b121042934() throws Exception {
+    R8TestCompileResult libraryResult = testForR8(backend)
+        .enableInliningAnnotations()
+        .addProgramClasses(LIBRARY_CLASSES)
+        .addKeepMainRule(LibrarySubclass.class)
+        .addKeepClassAndDefaultConstructor(LibrarySubclass.class)
+        .compile();
+
+    CodeInspector inspector = libraryResult.inspector();
+    assertThat(inspector.clazz(LibraryBase.class), not(isPresent()));
+    assertThat(inspector.clazz(LibrarySubclass.class), isPresent());
+
+    Path libraryOut = temp.newFolder().toPath().resolve("out.jar");
+    libraryResult.writeToZip(libraryOut);
+    testForR8(backend)
+        .noTreeShaking()
+        .noMinification()
+        .addProgramClasses(PROGRAM_CLASSES)
+        .addApplyMapping(libraryResult.getProguardMap())
+        .addLibraryClasses(LIBRARY_CLASSES)
+        .compile()
+        .addRunClasspath(Collections.singletonList(libraryOut))
+        .run(ProgramClass.class)
+        .assertFailureWithErrorThatMatches(containsString("NoSuchFieldError"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
new file mode 100644
index 0000000..9d2000e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
@@ -0,0 +1,110 @@
+// 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.naming.applymapping;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Collections;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ApplyMappingAfterVerticalMergingMethodTest extends TestBase {
+
+  // Base class will be vertical class merged into subclass
+  public static class LibraryBase {
+
+    @NeverInline
+    public String foo() {
+      return "LibraryBase::foo";
+    }
+  }
+
+  // Subclass targeted via vertical class merging. The main method ensures a reference to foo.
+  public static class LibrarySubclass extends LibraryBase {
+
+    public static void main(String[] args) {
+      System.out.println(new LibrarySubclass().foo());
+    }
+  }
+
+  // Program class that uses LibrarySubclass but the library does not explicitly keep foo and
+  // should thus fail at runtime.
+  public static class ProgramClass extends LibrarySubclass {
+
+    public static void main(String[] args) {
+      System.out.println(new ProgramClass().foo());
+    }
+  }
+
+  // Test runner code follows.
+
+  private static final Class<?>[] LIBRARY_CLASSES = {
+    NeverMerge.class, LibraryBase.class, LibrarySubclass.class
+  };
+
+  private static final Class<?>[] PROGRAM_CLASSES = {
+      ProgramClass.class
+  };
+
+  private Backend backend;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  public ApplyMappingAfterVerticalMergingMethodTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test
+  public void runOnJvm() throws Throwable {
+    Assume.assumeTrue(backend == Backend.CF);
+    testForJvm()
+        .addProgramClasses(LIBRARY_CLASSES)
+        .addProgramClasses(PROGRAM_CLASSES)
+        .run(ProgramClass.class)
+        .assertSuccessWithOutput(StringUtils.lines("LibraryBase::foo"));
+  }
+
+  @Test
+  public void b121042934() throws Exception {
+    R8TestCompileResult libraryResult = testForR8(backend)
+        .enableInliningAnnotations()
+        .addProgramClasses(LIBRARY_CLASSES)
+        .addKeepMainRule(LibrarySubclass.class)
+        .addKeepClassAndDefaultConstructor(LibrarySubclass.class)
+        .compile();
+
+    CodeInspector inspector = libraryResult.inspector();
+    assertThat(inspector.clazz(LibraryBase.class), not(isPresent()));
+    assertThat(inspector.clazz(LibrarySubclass.class), isPresent());
+
+    Path libraryOut = temp.newFolder().toPath().resolve("out.jar");
+    libraryResult.writeToZip(libraryOut);
+    testForR8(backend)
+        .noTreeShaking()
+        .noMinification()
+        .addProgramClasses(PROGRAM_CLASSES)
+        .addApplyMapping(libraryResult.getProguardMap())
+        .addLibraryClasses(LIBRARY_CLASSES)
+        .compile()
+        .addRunClasspath(Collections.singletonList(libraryOut))
+        .run(ProgramClass.class)
+        .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingTest.java
deleted file mode 100644
index 8856f47..0000000
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// 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.naming.applymapping;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import com.android.tools.r8.NeverMerge;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FieldSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.lang.ref.WeakReference;
-import java.nio.file.Path;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-class Resources {
-}
-
-@NeverMerge
-class ResourceWrapper extends Resources {
-  // Will be merged down, and represented as:
-  //     ...applymapping.Resources ...applymapping.ResourceWrapper.mResources -> a
-  private Resources mResources;
-
-  ResourceWrapper(Resources resource) {
-    this.mResources = resource;
-  }
-
-  // Will be merged down, and represented as:
-  //     java.lang.String ...applymapping.ResourceWrapper.foo() -> a
-  String foo() {
-    return mResources.toString();
-  }
-
-  @Override
-  public String toString() {
-    return mResources.toString();
-  }
-}
-
-class TintResources extends ResourceWrapper {
-  private WeakReference<Resources> ref;
-
-  TintResources(Resources resource) {
-    super(resource);
-    ref = new WeakReference<>(resource);
-  }
-
-  public static void main(String[] args) {
-    TintResources t = new TintResources(new Resources());
-    System.out.println(t.foo());
-  }
-}
-
-@RunWith(Parameterized.class)
-public class ApplyMappingAfterVerticalMergingTest extends TestBase {
-  private final static Class<?>[] CLASSES = {
-      NeverMerge.class, Resources.class, ResourceWrapper.class, TintResources.class
-  };
-  private final static Class<?> MAIN = TintResources.class;
-
-  private Backend backend;
-
-  @Parameterized.Parameters(name = "Backend: {0}")
-  public static Backend[] data() {
-    return Backend.values();
-  }
-
-  public ApplyMappingAfterVerticalMergingTest(Backend backend) {
-    this.backend = backend;
-  }
-
-  @Ignore("b/12042934")
-  @Test
-  public void b121042934() throws Exception {
-    Path mapPath = temp.newFile("test-mapping.txt").toPath();
-    CodeInspector inspector1 = testForR8(backend)
-        .addProgramClasses(CLASSES)
-        .addKeepMainRule(MAIN)
-        .addKeepRules("-printmapping " + mapPath.toAbsolutePath())
-        .compile()
-        .inspector();
-    CodeInspector inspector2 = testForR8(backend)
-        .addProgramClasses(CLASSES)
-        .addKeepMainRule(MAIN)
-        .addKeepRules("-applymapping " + mapPath.toAbsolutePath())
-        .compile()
-        .inspector();
-
-    ClassSubject classSubject1 = inspector1.clazz(MAIN);
-    assertThat(classSubject1, isPresent());
-    assertThat(classSubject1, isRenamed());
-    ClassSubject classSubject2 = inspector2.clazz(
-        DescriptorUtils.getClassNameFromDescriptor(classSubject1.getFinalDescriptor()));
-    assertThat(classSubject2, isPresent());
-    assertThat(classSubject2, not(isRenamed()));
-    assertEquals(classSubject1.getFinalDescriptor(), classSubject2.getFinalDescriptor());
-
-    FieldSubject field1 = classSubject1.uniqueFieldWithName("mResources");
-    assertThat(field1, isPresent());
-    assertThat(field1, isRenamed());
-    FieldSubject field2 = classSubject2.uniqueFieldWithName("mResources");
-    assertThat(field2, isPresent());
-    assertThat(field2, not(isRenamed()));
-    assertEquals(field1.getFinalSignature().toString(), field2.getFinalSignature().toString());
-
-    MethodSubject method1 = classSubject1.uniqueMethodWithName("foo");
-    assertThat(method1, isPresent());
-    assertThat(method1, isRenamed());
-    MethodSubject method2 = classSubject2.uniqueMethodWithName("foo");
-    assertThat(method2, isPresent());
-    assertThat(method2, not(isRenamed()));
-    assertEquals(method1.getFinalSignature().toString(), method2.getFinalSignature().toString());
-  }
-
-}