Ensure stable insertion of markers when writing DEX

Bug: b/243111584
Change-Id: I5cad64531179b7fe9b16f0ac4bdfc8025aa53d9b
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 8be1456..74a3d20 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -46,19 +46,16 @@
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -278,15 +275,6 @@
       boolean hasDexResources = appView.appInfo().app().getFlags().hasReadProgramClassFromDex();
 
       Marker marker = options.getMarker(Tool.D8);
-      Set<Marker> markers = new HashSet<>(appView.dexItemFactory().extractMarkers());
-      // TODO(b/166617364): Don't add an additional marker when desugaring is turned off.
-      if (hasClassResources
-          && (options.desugarState != DesugarState.OFF
-              || markers.isEmpty()
-              || (markers.size() == 1 && markers.iterator().next().isL8()))) {
-        markers.add(marker);
-      }
-      Marker.checkCompatibleDesugaredLibrary(markers, options.reporter);
 
       timing.time(
           "Run inspections",
@@ -314,7 +302,7 @@
         // without iterating again the IR. We fall-back to writing one app with rewriting and
         // merging it with the other app in rewriteNonDexInputs.
         timing.begin("Rewrite non-dex inputs");
-        DexApplication app = rewriteNonDexInputs(appView, inputApp, executor, timing);
+        DexApplication app = rewriteNonDexInputs(appView, inputApp, executor, marker, timing);
         timing.end();
         appView.setAppInfo(
             new AppInfo(
@@ -345,8 +333,7 @@
       if (options.isGeneratingClassFiles()) {
         new CfApplicationWriter(appView, marker).write(options.getClassFileConsumer(), inputApp);
       } else {
-        new ApplicationWriter(appView, marker == null ? null : ImmutableList.copyOf(markers))
-            .write(executor, inputApp);
+        new ApplicationWriter(appView, marker).write(executor, inputApp);
       }
       options.printWarnings();
     } catch (ExecutionException e) {
@@ -408,7 +395,11 @@
   }
 
   private static DexApplication rewriteNonDexInputs(
-      AppView<AppInfo> appView, AndroidApp inputApp, ExecutorService executor, Timing timing)
+      AppView<AppInfo> appView,
+      AndroidApp inputApp,
+      ExecutorService executor,
+      Marker marker,
+      Timing timing)
       throws IOException, ExecutionException {
     // TODO(b/154575955): Remove the naming lens in D8.
     appView
@@ -437,11 +428,7 @@
     ConvertedCfFiles convertedCfFiles = new ConvertedCfFiles();
     new GenericSignatureRewriter(appView).run(appView.appInfo().classes(), executor);
     new KotlinMetadataRewriter(appView).runForD8(executor);
-    new ApplicationWriter(
-            appView,
-            null,
-            convertedCfFiles)
-        .write(executor);
+    new ApplicationWriter(appView, marker, convertedCfFiles).write(executor);
     AndroidApp.Builder builder = AndroidApp.builder(inputApp);
     builder.getProgramResourceProviders().clear();
     builder.addProgramResourceProvider(convertedCfFiles);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 0e1eafb..038b821 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -112,7 +112,6 @@
 import com.android.tools.r8.utils.StringUtils;
 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.Iterables;
 import com.google.common.io.ByteStreams;
 import java.io.ByteArrayOutputStream;
@@ -123,7 +122,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -215,18 +213,10 @@
     try {
       Marker marker = options.getMarker(Tool.R8);
       assert marker != null;
-      // Get the markers from the input which are different from the one created for this
-      // compilation
-      Set<Marker> markers = new HashSet<>(appView.dexItemFactory().extractMarkers());
-      markers.remove(marker);
       if (options.isGeneratingClassFiles()) {
         new CfApplicationWriter(appView, marker).write(options.getClassFileConsumer(), inputApp);
       } else {
-        new ApplicationWriter(
-                appView,
-                // Ensure that the marker for this compilation is the first in the list.
-                ImmutableList.<Marker>builder().add(marker).addAll(markers).build())
-            .write(executorService, inputApp);
+        new ApplicationWriter(appView, marker).write(executorService, inputApp);
       }
     } catch (IOException e) {
       throw new RuntimeException("Cannot write application", e);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 845ca61..bb281b3 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -62,6 +63,7 @@
 import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer.InternalGlobalSyntheticsDexIndexedConsumer;
 import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer.InternalGlobalSyntheticsDexPerFileConsumer;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OriginalSourceFiles;
 import com.android.tools.r8.utils.PredicateUtils;
 import com.android.tools.r8.utils.Reporter;
@@ -77,10 +79,12 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -93,7 +97,8 @@
   public final InternalOptions options;
   private final CodeToKeep desugaredLibraryCodeToKeep;
   private final Predicate<DexType> isTypeMissing;
-  public List<Marker> markers;
+  private final Optional<Marker> currentMarker;
+  public Collection<Marker> previousMarkers;
   public List<DexString> markerStrings;
   public Set<VirtualFile> globalSyntheticFiles;
 
@@ -169,21 +174,19 @@
     }
   }
 
-  public ApplicationWriter(AppView<?> appView, List<Marker> markers) {
-    this(appView, markers, null);
+  public ApplicationWriter(AppView<?> appView, Marker marker) {
+    this(appView, marker, null);
   }
 
-  public ApplicationWriter(
-      AppView<?> appView,
-      List<Marker> markers,
-      DexIndexedConsumer consumer) {
+  public ApplicationWriter(AppView<?> appView, Marker marker, DexIndexedConsumer consumer) {
     this.appView = appView;
     this.options = appView.options();
     this.desugaredLibraryCodeToKeep = CodeToKeep.createCodeToKeep(appView);
-    this.markers = markers;
+    this.currentMarker = Optional.ofNullable(marker);
     this.programConsumer = consumer;
     this.isTypeMissing =
         PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
+    this.previousMarkers = appView.dexItemFactory().extractMarkers();
   }
 
   private NamingLens getNamingLens() {
@@ -316,8 +319,8 @@
         encodeChecksums(virtualFiles);
         timing.end();
       }
-      assert markers == null
-          || markers.isEmpty()
+      assert previousMarkers == null
+          || previousMarkers.isEmpty()
           || appView.dexItemFactory().extractMarkers() != null;
       assert appView.withProtoShrinker(
           shrinker -> virtualFiles.stream().allMatch(shrinker::verifyDeadProtoTypesNotReferenced),
@@ -404,26 +407,29 @@
 
   private void computeMarkerStrings(
       Box<ProguardMapId> delayedProguardMapId, List<LazyDexString> lazyDexStrings) {
-    if (markers != null && !markers.isEmpty()) {
-      int firstNonLazyMarker = 0;
-      if (willComputeProguardMap()) {
-        firstNonLazyMarker++;
-        lazyDexStrings.add(
-            new LazyDexString() {
-
-              @Override
-              public DexString internalCompute() {
-                Marker marker = markers.get(0);
-                marker.setPgMapId(delayedProguardMapId.get().getId());
-                return marker.toDexString(appView.dexItemFactory());
-              }
-            });
-      }
-      markerStrings = new ArrayList<>(markers.size() - firstNonLazyMarker);
-      for (int i = firstNonLazyMarker; i < markers.size(); i++) {
-        markerStrings.add(markers.get(i).toDexString(appView.dexItemFactory()));
-      }
+    List<Marker> allMarkers = new ArrayList<>();
+    if (previousMarkers != null) {
+      allMarkers.addAll(previousMarkers);
     }
+    DexItemFactory factory = appView.dexItemFactory();
+    currentMarker.ifPresent(
+        marker -> {
+          if (willComputeProguardMap()) {
+            lazyDexStrings.add(
+                new LazyDexString() {
+
+                  @Override
+                  public DexString internalCompute() {
+                    marker.setPgMapId(delayedProguardMapId.get().getId());
+                    return marker.toDexString(factory);
+                  }
+                });
+          } else {
+            allMarkers.add(marker);
+          }
+        });
+    allMarkers.sort(Comparator.comparing(Marker::toString));
+    markerStrings = ListUtils.map(allMarkers, marker -> marker.toDexString(factory));
   }
 
   private OriginalSourceFiles computeSourceFileString(
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index f2b1e15..b308d5d 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -4,20 +4,15 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.errors.DesugaredLibraryMismatchDiagnostic;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import com.google.gson.JsonSyntaxException;
 import java.util.Comparator;
-import java.util.HashSet;
 import java.util.Map.Entry;
-import java.util.Set;
 
 /** Abstraction for hidden dex marker intended for the main dex file. */
 public class Marker {
@@ -68,55 +63,6 @@
     this.jsonObject = jsonObject;
   }
 
-  public static void checkCompatibleDesugaredLibrary(Set<Marker> markers, Reporter reporter) {
-    if (markers.size() <= 1) {
-      return;
-    }
-    // In L8 compilation, the compilation has two markers, a L8 marker, which has a desugared
-    // library property, and either a D8 or a R8 marker, which has no desugared library property.
-    // In other compilations, the desugared library versions have to be consistent.
-    Set<String> desugaredLibraryIdentifiers = new HashSet<>();
-    for (Marker marker : markers) {
-      if (marker.tool == Tool.L8) {
-        assert marker.getDesugaredLibraryIdentifiers().length > 0;
-        assert markers.stream()
-            .allMatch(m -> m.tool == Tool.L8 || m.getDesugaredLibraryIdentifiers().length == 0);
-      } else {
-        String[] identifiers = marker.getDesugaredLibraryIdentifiers();
-        String identifier = null;
-        switch (identifiers.length) {
-          case 0:
-            // Only add the <no-library-desugaring> identifier for DEX. A marker from CF is
-            // assumed to go though desugaring for compiling to DEX, and that will introduce the
-            // DEX marker with the final library desugaring identifier.
-            if (marker.isDexBackend()) {
-              identifier = NO_LIBRARY_DESUGARING;
-            } else {
-              assert marker.isCfBackend();
-            }
-            break;
-          case 1:
-            identifier = identifiers[0];
-            break;
-          default:
-            // To be implemented once D8/R8 compilation supports multiple desugared libraries.
-            throw reporter.fatalError(
-                new StringDiagnostic(
-                    "Merging program compiled with multiple desugared libraries."));
-        }
-        if (marker.isDesugared() && identifier != null) {
-          desugaredLibraryIdentifiers.add(identifier);
-        } else {
-          assert identifier == null || identifier.equals(NO_LIBRARY_DESUGARING);
-        }
-      }
-    }
-
-    if (desugaredLibraryIdentifiers.size() > 1) {
-      reporter.error(new DesugaredLibraryMismatchDiagnostic(desugaredLibraryIdentifiers, markers));
-    }
-  }
-
   public Tool getTool() {
     return tool;
   }
diff --git a/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java b/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
index 5141753..f23cee7 100644
--- a/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
@@ -8,16 +8,17 @@
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
+import java.util.Collection;
 import java.util.Set;
 import java.util.stream.Collectors;
 
 public class DesugaredLibraryMismatchDiagnostic implements Diagnostic {
 
   private final Set<String> desugaredLibraryIdentifiers;
-  private final Set<Marker> markers;
+  private final Collection<Marker> markers;
 
   public DesugaredLibraryMismatchDiagnostic(
-      Set<String> desugaredLibraryIdentifiers, Set<Marker> markers) {
+      Set<String> desugaredLibraryIdentifiers, Collection<Marker> markers) {
     this.desugaredLibraryIdentifiers = desugaredLibraryIdentifiers;
     this.markers = markers;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
index 3fe50aa..be0c645 100644
--- a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
@@ -12,7 +12,7 @@
 import java.io.PrintStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.List;
+import java.util.Collection;
 import java.util.function.Consumer;
 
 public abstract class DexByteCodeWriter {
@@ -49,7 +49,7 @@
   }
 
   public void writeMarkers(PrintStream output) {
-    List<Marker> markers = application.dexItemFactory.extractMarkers();
+    Collection<Marker> markers = application.dexItemFactory.extractMarkers();
     System.out.println("Number of markers: " + markers.size());
     for (Marker marker : markers) {
       output.println(marker.toString());
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 bfa111e..b10a4f5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -52,7 +52,9 @@
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -2598,9 +2600,9 @@
 
   // Debugging support to extract marking string.
   // Find all markers.
-  public synchronized List<Marker> extractMarkers() {
+  public synchronized Collection<Marker> extractMarkers() {
     // This is slow but it is not needed for any production code yet.
-    List<Marker> markers = new ArrayList<>();
+    Set<Marker> markers = new HashSet<>();
     for (DexString dexString : strings.keySet()) {
       Marker marker = Marker.parse(dexString);
       if (marker != null) {
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 6586750..461dc2a 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -90,7 +90,7 @@
   private final DexApplication application;
   private final AppView<?> appView;
   private final InternalOptions options;
-  private final Marker marker;
+  private final Optional<Marker> marker;
   private final Predicate<DexType> isTypeMissing;
 
   private static final CfVersion MIN_VERSION_FOR_COMPILER_GENERATED_CODE = CfVersion.V1_6;
@@ -99,8 +99,7 @@
     this.application = appView.appInfo().app();
     this.appView = appView;
     this.options = appView.options();
-    assert marker != null;
-    this.marker = marker;
+    this.marker = Optional.ofNullable(marker);
     this.isTypeMissing =
         PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
   }
@@ -137,6 +136,7 @@
   private void writeApplication(AndroidApp inputApp, ClassFileConsumer consumer) {
     ProguardMapId proguardMapId = null;
     if (options.proguardMapConsumer != null) {
+      assert marker.isPresent();
       proguardMapId =
           runAndWriteMap(
               inputApp,
@@ -144,10 +144,9 @@
               application.timing,
               OriginalSourceFiles.fromClasses(),
               DebugRepresentation.none(options));
-      marker.setPgMapId(proguardMapId.getId());
+      marker.get().setPgMapId(proguardMapId.getId());
     }
-    Optional<String> markerString =
-        includeMarker(marker) ? Optional.of(marker.toString()) : Optional.empty();
+    Optional<String> markerString = marker.filter(this::includeMarker).map(Marker::toString);
     SourceFileEnvironment sourceFileEnvironment = null;
     if (options.sourceFileProvider != null) {
       sourceFileEnvironment = ApplicationWriter.createSourceFileEnvironment(proguardMapId);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
index be8001a..de28b58 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
@@ -14,14 +14,10 @@
 import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8TestCompileResult;
-import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.ExtractMarker;
 import com.android.tools.r8.LibraryDesugaringTestConfiguration;
 import com.android.tools.r8.TestDiagnosticMessages;
@@ -63,35 +59,39 @@
 
   @Test
   public void testMergeDesugaredAndNonDesugared() throws Exception {
-    D8TestCompileResult compileResult;
-    try {
-      compileResult =
-          testForD8()
-              .addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles())
-              .addProgramFiles(buildPart1DesugaredLibrary(), buildPart2NoDesugaredLibrary())
-              .setMinApi(parameters.getApiLevel())
-              .applyIf(
-                  someLibraryDesugaringRequired(),
-                  b ->
-                      b.enableCoreLibraryDesugaring(
-                          LibraryDesugaringTestConfiguration.forSpecification(
-                              libraryDesugaringSpecification.getSpecification())))
-              .compileWithExpectedDiagnostics(this::assertError)
-              .addRunClasspathFiles(
-                  getNonShrunkDesugaredLib(parameters, libraryDesugaringSpecification));
-      assertFalse(expectError());
-    } catch (CompilationFailedException e) {
-      assertTrue(expectError());
-      return;
-    }
-    assert !expectError();
-    assert compileResult != null;
+    D8TestCompileResult compileResult =
+        testForD8()
+            .addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles())
+            .addProgramFiles(buildPart1DesugaredLibrary(), buildPart2NoDesugaredLibrary())
+            .setMinApi(parameters.getApiLevel())
+            .applyIf(
+                someLibraryDesugaringRequired(),
+                b ->
+                    b.enableCoreLibraryDesugaring(
+                        LibraryDesugaringTestConfiguration.forSpecification(
+                            libraryDesugaringSpecification.getSpecification())))
+            .compile()
+            .addRunClasspathFiles(
+                getNonShrunkDesugaredLib(parameters, libraryDesugaringSpecification));
     compileResult
         .run(parameters.getRuntime(), Part1.class)
-        .assertSuccessWithOutputLines(JAVA_RESULT);
+        .assertSuccessWithOutputLines(getExpected());
     compileResult
         .run(parameters.getRuntime(), Part2.class)
-        .assertSuccessWithOutputLines(JAVA_RESULT);
+        .assertFailureWithErrorThatThrowsIf(!isApiAvailable(), NoSuchMethodError.class)
+        .assertSuccessWithOutputLinesIf(isApiAvailable(), JAVA_RESULT);
+  }
+
+  private boolean isApiAvailable() {
+    return parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel();
+  }
+
+  private String getExpected() {
+    if (isApiAvailable()) {
+      return JAVA_RESULT;
+    } else {
+      return J$_RESULT;
+    }
   }
 
   @Test
@@ -169,9 +169,17 @@
             .writeToZip();
 
     // D8 dex file output marker has the same marker as the D8 class file output.
-    // TODO(b/166617364): They should not be the same after backend is recorded and neither has
-    //  library desugaring info.
-    assertMarkersMatch(ExtractMarker.extractMarkerFromJarFile(desugaredLibDex), markerMatcher);
+    Matcher<Marker> dexMarkerMatcher =
+        allOf(
+            markerTool(Tool.D8),
+            markerCompilationMode(CompilationMode.DEBUG),
+            markerBackend(Backend.DEX),
+            markerIsDesugared(),
+            markerMinApi(parameters.getApiLevel()),
+            not(markerHasDesugaredLibraryIdentifier()));
+    List<Matcher<Marker>> markerMatcherAfterDex = ImmutableList.of(markerMatcher, dexMarkerMatcher);
+    assertMarkersMatch(
+        ExtractMarker.extractMarkerFromJarFile(desugaredLibDex), markerMatcherAfterDex);
 
     // Build an app using library desugaring merging with library not using library desugaring.
     Path app;
@@ -183,36 +191,20 @@
             .writeToZip();
 
     // When there is no class-file resources we are adding the marker for the last compilation.
-    assertMarkersMatch(
-        ExtractMarker.extractMarkerFromDexFile(app),
-        ImmutableList.of(
-            markerMatcher,
-            allOf(
-                markerTool(Tool.D8),
-                markerCompilationMode(CompilationMode.DEBUG),
-                markerBackend(Backend.DEX),
-                markerIsDesugared(),
-                markerMinApi(parameters.getApiLevel()),
-                someLibraryDesugaringRequired()
-                    ? markerHasDesugaredLibraryIdentifier()
-                    : not(markerHasDesugaredLibraryIdentifier()))));
-  }
-
-  private void assertError(TestDiagnosticMessages m) {
-    List<Diagnostic> errors = m.getErrors();
-    if (expectError()) {
-      assertEquals(1, errors.size());
-      assertTrue(
-          errors.stream()
-              .anyMatch(
-                  w ->
-                      w.getDiagnosticMessage()
-                          .contains(
-                              "The compilation is merging inputs with different"
-                                  + " desugared library desugaring")));
-    } else {
-      assertEquals(0, errors.size());
+    List<Matcher<Marker>> expectedMarkers = new ArrayList<>();
+    expectedMarkers.add(markerMatcher);
+    expectedMarkers.add(dexMarkerMatcher);
+    if (someLibraryDesugaringRequired()) {
+      expectedMarkers.add(
+          allOf(
+              markerTool(Tool.D8),
+              markerCompilationMode(CompilationMode.DEBUG),
+              markerBackend(Backend.DEX),
+              markerIsDesugared(),
+              markerMinApi(parameters.getApiLevel()),
+              markerHasDesugaredLibraryIdentifier()));
     }
+    assertMarkersMatch(ExtractMarker.extractMarkerFromDexFile(app), expectedMarkers);
   }
 
   private boolean expectError() {
@@ -241,21 +233,12 @@
             .addRunClasspathFiles(
                 getNonShrunkDesugaredLib(parameters, libraryDesugaringSpecification))
             .inspectDiagnosticMessages(this::assertWarningPresent);
-    if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
-      compileResult
-          .run(parameters.getRuntime(), Part1.class)
-          .assertSuccessWithOutputLines(J$_RESULT);
-      compileResult
-          .run(parameters.getRuntime(), Part2.class)
-          .assertSuccessWithOutputLines(J$_RESULT);
-    } else {
-      compileResult
-          .run(parameters.getRuntime(), Part1.class)
-          .assertSuccessWithOutputLines(JAVA_RESULT);
-      compileResult
-          .run(parameters.getRuntime(), Part2.class)
-          .assertSuccessWithOutputLines(JAVA_RESULT);
-    }
+    compileResult
+        .run(parameters.getRuntime(), Part1.class)
+        .assertSuccessWithOutputLines(getExpected());
+    compileResult
+        .run(parameters.getRuntime(), Part2.class)
+        .assertSuccessWithOutputLines(getExpected());
   }
 
   private void assertWarningPresent(TestDiagnosticMessages testDiagnosticMessages) {