[Retrace] Add support for partitioning without metadata

Bug: b/201666766
Bug: b/211603371
Change-Id: Ib20df2c8120ba4a4dbfe64d3c51437b113fd2609
diff --git a/src/main/java/com/android/tools/r8/naming/MapVersion.java b/src/main/java/com/android/tools/r8/naming/MapVersion.java
index a23196c..ae93504 100644
--- a/src/main/java/com/android/tools/r8/naming/MapVersion.java
+++ b/src/main/java/com/android/tools/r8/naming/MapVersion.java
@@ -3,8 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.Keep;
 import com.android.tools.r8.utils.structural.Ordered;
 
+@Keep
 public enum MapVersion implements Ordered<MapVersion> {
   MAP_VERSION_NONE("none"),
   MAP_VERSION_1_0("1.0"),
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartitionFromKeySupplier.java b/src/main/java/com/android/tools/r8/retrace/MappingPartitionFromKeySupplier.java
new file mode 100644
index 0000000..9b8c0a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingPartitionFromKeySupplier.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2022, 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.retrace;
+
+import com.android.tools.r8.Keep;
+
+/***
+ * Supplier to provide the bytes for a partition specified by a key. Retrace guarantee that the
+ * supplier is always called after a call with the key to {@link RegisterMappingPartitionCallback}
+ * and a call to {@link PrepareMappingPartitionsCallback}
+ *
+ */
+@FunctionalInterface
+@Keep
+public interface MappingPartitionFromKeySupplier {
+
+  byte[] get(String key);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java
index cec8467..d56aee5 100644
--- a/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java
+++ b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java
@@ -5,20 +5,56 @@
 package com.android.tools.r8.retrace;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.retrace.internal.PartitionMappingSupplierBuilderImpl;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 @Keep
 public abstract class PartitionMappingSupplier extends MappingSupplier<PartitionMappingSupplier> {
 
   public static Builder builder() {
-    return new PartitionMappingSupplierBuilderImpl();
+    return new PartitionMappingSupplierBuilderImpl(MapVersion.MAP_VERSION_NONE);
+  }
+
+  public static NoMetadataBuilder<?> noMetadataBuilder(MapVersion mapVersion) {
+    return new PartitionMappingSupplierBuilderImpl(mapVersion);
   }
 
   @Keep
-  public abstract static class Builder
-      extends MappingSupplierBuilder<PartitionMappingSupplier, Builder> {
+  public abstract static class NoMetadataBuilder<B extends NoMetadataBuilder<B>>
+      extends MappingSupplierBuilder<PartitionMappingSupplier, B> {
+
+    /***
+     * Callback to be notified of a partition that is later going to be needed. When all needed
+     * partitions are found the callback specified to {@code setPrepareMappingPartitionsCallback} is
+     * called.
+     *
+     * @param registerPartitionCallback the consumer to get keys for partitions.
+     */
+    public abstract Builder setRegisterMappingPartitionCallback(
+        RegisterMappingPartitionCallback registerPartitionCallback);
+
+    /***
+     * A callback notifying that all partitions should be prepared. The prepare callback is
+     * guaranteed to be called prior to any calls to the partition supplier.
+     *
+     * @param prepare the callback to listen for when partitions should be prepared.
+     */
+    public abstract Builder setPrepareMappingPartitionsCallback(
+        PrepareMappingPartitionsCallback prepare);
+
+    /***
+     * Set the partition supplier that is needed for retracing. All partitions needed has been
+     * declared earlier and this should block until the bytes associated with the partition is
+     * ready.
+     *
+     * @param partitionSupplier the function to return a partition to retrace
+     */
+    public abstract Builder setMappingPartitionFromKeySupplier(
+        MappingPartitionFromKeySupplier partitionSupplier);
+  }
+
+  @Keep
+  public abstract static class Builder extends NoMetadataBuilder<Builder> {
 
     /***
      * Sets the serialized metadata that was obtained when partitioning.
@@ -26,30 +62,5 @@
      * @param metadata the serialized metadata
      */
     public abstract Builder setMetadata(byte[] metadata);
-
-    /***
-     * Callback to be notified of a partition that is later going to be needed. When all needed
-     * partitions has been found, the callback specified to {@code setPrepareNewPartitions} will
-     * be called.
-     *
-     * @param partitionToFetchConsumer the consumer to get keys for partitions.
-     */
-    public abstract Builder setPartitionToFetchConsumer(Consumer<String> partitionToFetchConsumer);
-
-    /***
-     * A callback notifying that all partitions should be prepared.
-     *
-     * @param run the callback to listen for when partitions should be prepared.
-     */
-    public abstract Builder setPrepareNewPartitions(Runnable run);
-
-    /***
-     * Set the partition supplier that is needed for retracing. All partitions needed has been
-     * declared earlier and this should block until the bytes associated with the partition is
-     * ready.
-     *
-     * @param supplier the function to return a partition to retrace
-     */
-    public abstract Builder setPartitionSupplier(Function<String, byte[]> supplier);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java b/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java
new file mode 100644
index 0000000..b19babd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2022, 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.retrace;
+
+import com.android.tools.r8.Keep;
+
+/***
+ * Callback to be called to prepare all partitions registered by
+ * {@link RegisterMappingPartitionCallback}. This is guaranteed to be called before calling
+ * {@link MappingPartitionFromKeySupplier}.
+ */
+@FunctionalInterface
+@Keep
+public interface PrepareMappingPartitionsCallback {
+
+  void prepare();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java b/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java
new file mode 100644
index 0000000..df5c943
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2022, 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.retrace;
+
+import com.android.tools.r8.Keep;
+
+/***
+ * Interface for registering a mapping partition to be used later.
+ */
+@FunctionalInterface
+@Keep
+public interface RegisterMappingPartitionCallback {
+
+  void register(String key);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java
index f100f97..2f69645 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java
@@ -4,19 +4,25 @@
 
 package com.android.tools.r8.retrace.internal;
 
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.MappingPartitionFromKeySupplier;
 import com.android.tools.r8.retrace.PartitionMappingSupplier;
-import com.android.tools.r8.utils.ConsumerUtils;
-import java.util.function.Consumer;
-import java.util.function.Function;
+import com.android.tools.r8.retrace.PrepareMappingPartitionsCallback;
+import com.android.tools.r8.retrace.RegisterMappingPartitionCallback;
 
 public class PartitionMappingSupplierBuilderImpl extends PartitionMappingSupplier.Builder {
 
-  private Function<String, byte[]> partitionSupplier;
-  private Consumer<String> partitionToFetchConsumer = ConsumerUtils.emptyConsumer();
-  private Runnable prepare = () -> {};
+  private MappingPartitionFromKeySupplier partitionSupplier;
+  private RegisterMappingPartitionCallback registerPartitionCallback = key -> {};
+  private PrepareMappingPartitionsCallback prepare = () -> {};
   private byte[] metadata;
+  private final MapVersion fallbackMapVersion;
   private boolean allowExperimental = false;
 
+  public PartitionMappingSupplierBuilderImpl(MapVersion fallbackMapVersion) {
+    this.fallbackMapVersion = fallbackMapVersion;
+  }
+
   @Override
   public PartitionMappingSupplier.Builder self() {
     return this;
@@ -35,28 +41,38 @@
   }
 
   @Override
-  public PartitionMappingSupplier.Builder setPartitionSupplier(
-      Function<String, byte[]> partitionSupplier) {
-    this.partitionSupplier = partitionSupplier;
+  public PartitionMappingSupplier.Builder setRegisterMappingPartitionCallback(
+      RegisterMappingPartitionCallback registerPartitionCallback) {
+    this.registerPartitionCallback = registerPartitionCallback;
     return self();
   }
 
   @Override
-  public PartitionMappingSupplier.Builder setPartitionToFetchConsumer(
-      Consumer<String> partitionToFetchConsumer) {
-    this.partitionToFetchConsumer = partitionToFetchConsumer;
-    return self();
-  }
-
-  @Override
-  public PartitionMappingSupplier.Builder setPrepareNewPartitions(Runnable prepare) {
+  public PartitionMappingSupplier.Builder setPrepareMappingPartitionsCallback(
+      PrepareMappingPartitionsCallback prepare) {
     this.prepare = prepare;
     return self();
   }
 
   @Override
+  public PartitionMappingSupplier.Builder setMappingPartitionFromKeySupplier(
+      MappingPartitionFromKeySupplier partitionSupplier) {
+    this.partitionSupplier = partitionSupplier;
+    return self();
+  }
+
+  @Override
   public PartitionMappingSupplier build() {
+    if (partitionSupplier == null) {
+      throw new RuntimeException(
+          "Cannot build without providing a mapping partition from key supplier.");
+    }
     return new PartitionMappingSupplierImpl(
-        metadata, partitionToFetchConsumer, prepare, partitionSupplier, allowExperimental);
+        metadata,
+        registerPartitionCallback,
+        prepare,
+        partitionSupplier,
+        allowExperimental,
+        fallbackMapVersion);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
index 7d7708d..dfddd37 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
@@ -15,7 +15,10 @@
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.retrace.InvalidMappingFileException;
+import com.android.tools.r8.retrace.MappingPartitionFromKeySupplier;
 import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.PrepareMappingPartitionsCallback;
+import com.android.tools.r8.retrace.RegisterMappingPartitionCallback;
 import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
 import com.android.tools.r8.utils.StringDiagnostic;
 import java.io.ByteArrayInputStream;
@@ -24,8 +27,6 @@
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 /**
  * IntelliJ highlights the class as being invalid because it cannot see getClassNameMapper is
@@ -34,10 +35,11 @@
 public class PartitionMappingSupplierImpl extends PartitionMappingSupplier {
 
   private final byte[] metadata;
-  private final Consumer<String> partitionToFetchConsumer;
-  private final Runnable prepare;
-  private final Function<String, byte[]> partitionSupplier;
+  private final RegisterMappingPartitionCallback registerPartitionCallback;
+  private final PrepareMappingPartitionsCallback prepare;
+  private final MappingPartitionFromKeySupplier partitionSupplier;
   private final boolean allowExperimental;
+  private final MapVersion fallbackMapVersion;
 
   private ClassNameMapper classNameMapper;
   private final Set<String> pendingKeys = new LinkedHashSet<>();
@@ -47,21 +49,26 @@
 
   PartitionMappingSupplierImpl(
       byte[] metadata,
-      Consumer<String> partitionToFetchConsumer,
-      Runnable prepare,
-      Function<String, byte[]> partitionSupplier,
-      boolean allowExperimental) {
+      RegisterMappingPartitionCallback registerPartitionCallback,
+      PrepareMappingPartitionsCallback prepare,
+      MappingPartitionFromKeySupplier partitionSupplier,
+      boolean allowExperimental,
+      MapVersion fallbackMapVersion) {
     this.metadata = metadata;
-    this.partitionToFetchConsumer = partitionToFetchConsumer;
+    this.registerPartitionCallback = registerPartitionCallback;
     this.prepare = prepare;
     this.partitionSupplier = partitionSupplier;
     this.allowExperimental = allowExperimental;
+    this.fallbackMapVersion = fallbackMapVersion;
   }
 
   private MappingPartitionMetadataInternal getMetadata(DiagnosticsHandler diagnosticsHandler) {
+    if (mappingPartitionMetadataCache != null) {
+      return mappingPartitionMetadataCache;
+    }
     return mappingPartitionMetadataCache =
         MappingPartitionMetadataInternal.createFromBytes(
-            metadata, MapVersion.MAP_VERSION_NONE, diagnosticsHandler);
+            metadata, fallbackMapVersion, diagnosticsHandler);
   }
 
   @Override
@@ -86,13 +93,13 @@
   private ClassNameMapper getClassNameMapper(DiagnosticsHandler diagnosticsHandler) {
     MappingPartitionMetadataInternal metadata = getMetadata(diagnosticsHandler);
     if (!pendingKeys.isEmpty()) {
-      prepare.run();
+      prepare.prepare();
     }
     for (String pendingKey : pendingKeys) {
       try {
         LineReader reader =
             new ProguardMapReaderWithFilteringInputBuffer(
-                new ByteArrayInputStream(partitionSupplier.apply(pendingKey)), alwaysTrue(), true);
+                new ByteArrayInputStream(partitionSupplier.get(pendingKey)), alwaysTrue(), true);
         classNameMapper =
             ClassNameMapper.mapperFromLineReaderWithFiltering(
                     reader, metadata.getMapVersion(), diagnosticsHandler, true, allowExperimental)
@@ -118,7 +125,7 @@
 
   private void registerKeyUse(String key) {
     if (!builtKeys.contains(key) && pendingKeys.add(key)) {
-      partitionToFetchConsumer.accept(key);
+      registerPartitionCallback.register(key);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
index 731fc0b..ffb522b 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
@@ -40,7 +40,9 @@
           RetraceApiInlineInOutlineTest.ApiTest.class,
           RetraceApiSingleFrameTest.ApiTest.class,
           RetracePartitionStringTest.ApiTest.class,
-          RetracePartitionRoundTripTest.ApiTest.class);
+          RetracePartitionRoundTripTest.ApiTest.class,
+          RetracePartitionJoinNoMetadataTest.ApiTest.class,
+          RetracePartitionSerializedObfuscatedKeyTest.ApiTest.class);
 
   public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
       ImmutableList.of();
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionJoinNoMetadataTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionJoinNoMetadataTest.java
new file mode 100644
index 0000000..095eb99
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionJoinNoMetadataTest.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2022, 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceCommand;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionJoinNoMetadataTest extends RetraceApiTestBase {
+
+  public RetracePartitionJoinNoMetadataTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String aMapping =
+        "com.foo.bar.baz -> a:\n"
+            + " # {'id':'sourceFile','fileName':'BarBaz.kt'}\n"
+            + "  int field -> c\n"
+            + "  1:1:void method():42:42 -> d";
+
+    private final String bMapping =
+        "foo -> foo:\n"
+            + " # {'id':'sourceFile','fileName':'Foo-inlinee.kt'}\n"
+            + "com.android.google.R8 -> b:\n"
+            + " # {'id':'sourceFile','fileName':'R8Car.kt'}\n"
+            + "  2:2:int foo.inlinee():43:43 -> f\n"
+            + "  2:2:int otherMethod():44 -> f\n"
+            + "# otherCommentHere";
+
+    private final String exceptionMapping = "com.android.google.exception -> exception:\n";
+
+    private final List<String> stackTrace =
+        Arrays.asList("exception: Hello World!", " at a.d(Unknown:1)", " at b.f(Unknown:2)");
+
+    private final List<String> expectedRetracedStackTrace =
+        Arrays.asList(
+            "com.android.google.exception: Hello World!",
+            " at com.foo.bar.baz.method(BarBaz.kt:42)",
+            " at foo.inlinee(Foo-inlinee.kt:43)",
+            " at com.android.google.R8.otherMethod(R8Car.kt:44)");
+
+    @Test
+    public void test() throws IOException {
+      List<String> preFetch = new ArrayList<>();
+      PartitionMappingSupplier mappingSupplier =
+          PartitionMappingSupplier.noMetadataBuilder(MapVersion.MAP_VERSION_1_0)
+              .setMappingPartitionFromKeySupplier(
+                  key -> {
+                    switch (key) {
+                      case "a":
+                        return aMapping.getBytes();
+                      case "b":
+                        return bMapping.getBytes();
+                      case "exception":
+                        return exceptionMapping.getBytes();
+                      default:
+                        fail();
+                        return null;
+                    }
+                  })
+              .setRegisterMappingPartitionCallback(preFetch::add)
+              .build();
+      Retrace.run(
+          RetraceCommand.builder()
+              .setStackTrace(stackTrace)
+              .setMappingSupplier(mappingSupplier)
+              .setRetracedStackTraceConsumer(
+                  retraced -> assertEquals(expectedRetracedStackTrace, retraced))
+              .build());
+      assertEquals(3, preFetch.size());
+      Set<String> expected = new HashSet<>(Arrays.asList("a", "b", "exception"));
+      assertEquals(expected, new HashSet<>(preFetch));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java
index 6a91dab..0eac918 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java
@@ -89,9 +89,9 @@
       PartitionMappingSupplier mappingSupplier =
           PartitionMappingSupplier.builder()
               .setMetadata(metadataData.getBytes())
-              .setPartitionToFetchConsumer(preFetchedKeys::add)
-              .setPrepareNewPartitions(() -> prepareCounter++)
-              .setPartitionSupplier(
+              .setRegisterMappingPartitionCallback(preFetchedKeys::add)
+              .setPrepareMappingPartitionsCallback(() -> prepareCounter++)
+              .setMappingPartitionFromKeySupplier(
                   key -> {
                     assertTrue(preFetchedKeys.contains(key));
                     return partitions.get(key);
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionSerializedObfuscatedKeyTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionSerializedObfuscatedKeyTest.java
new file mode 100644
index 0000000..58b092b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionSerializedObfuscatedKeyTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2022, 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceCommand;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionSerializedObfuscatedKeyTest extends RetraceApiTestBase {
+
+  public RetracePartitionSerializedObfuscatedKeyTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String mapping = "com.R8 -> a:\n" + "  void m() -> b\n";
+
+    // Serialized data from ObfuscatedTypeNameAsKeyMetadata with Map Version 1.0.
+    private final byte[] metadata = new byte[] {0, 0, 49, 46, 48};
+
+    private static final String minifiedStackTraceLine = " at a.b()";
+
+    private static final String retracedStackTraceLine = " at com.R8.m(R8.java)";
+
+    @Test
+    public void test() throws IOException {
+      PartitionMappingSupplier mappingSupplier =
+          PartitionMappingSupplier.builder()
+              .setMetadata(metadata)
+              .setMappingPartitionFromKeySupplier(
+                  key -> {
+                    assertEquals("a", key);
+                    return mapping.getBytes(StandardCharsets.UTF_8);
+                  })
+              .build();
+
+      List<String> stackTrace = new ArrayList<>();
+      stackTrace.add(minifiedStackTraceLine);
+
+      Retrace.run(
+          RetraceCommand.builder()
+              .setMappingSupplier(mappingSupplier)
+              .setStackTrace(stackTrace)
+              .setRetracedStackTraceConsumer(
+                  retraced -> {
+                    assertEquals(1, retraced.size());
+                    assertEquals(retracedStackTraceLine, retraced.get(0));
+                  })
+              .build());
+    }
+  }
+}
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index f5a5521..0ebc3f5 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-3aa48c8d23b8ae1e1c51c91ef8c73f7bc7cb4657
\ No newline at end of file
+f49f174fe4f8ecf0ddc4b47d98bcb293d689eeb9
\ No newline at end of file