Fix ServiceLoader tests checking the wrong META-INF files

The tests were not taking into account class renaming / merging, and
that META-INF files are not actually removed.

Also adds a test for ServiceLoader where no META-INF is present.

Bug: 291923475
Change-Id: I7ed52413ad58a65bfe57ba662eb6ca90980c37c3
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderClassLoaderRewritingTest.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderClassLoaderRewritingTest.java
index 1df6b3a..9695e90 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderClassLoaderRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderClassLoaderRewritingTest.java
@@ -4,25 +4,17 @@
 
 package com.android.tools.r8.optimize.serviceloader;
 
-import static junit.framework.TestCase.assertNull;
-
-import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.StringUtils;
-import java.nio.file.Path;
 import java.util.ServiceLoader;
-import java.util.zip.ZipFile;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
 public class ServiceLoaderClassLoaderRewritingTest extends ServiceLoaderTestBase {
-
-  private final TestParameters parameters;
   private final String EXPECTED_OUTPUT = StringUtils.lines("Hello World!");
 
   public interface Service {
@@ -67,30 +59,17 @@
   }
 
   public ServiceLoaderClassLoaderRewritingTest(TestParameters parameters) {
-    this.parameters = parameters;
+    super(parameters);
   }
 
   @Test
   public void testRewritings() throws Exception {
-    Path path = temp.newFile("out.zip").toPath();
-    testForR8(parameters.getBackend())
-        .addInnerClasses(ServiceLoaderClassLoaderRewritingTest.class)
+    serviceLoaderTest(Service.class, ServiceImpl.class)
         .addKeepMainRule(MainRunner.class)
-        .setMinApi(parameters)
         .enableInliningAnnotations()
-        .addDataEntryResources(
-            DataEntryResource.fromBytes(
-                StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
-                "META-INF/services/" + Service.class.getTypeName(),
-                Origin.unknown()))
         .compile()
-        .writeToZip(path)
         .inspect(inspector -> verifyNoServiceLoaderLoads(inspector.clazz(MainRunner.class)))
         .run(parameters.getRuntime(), MainRunner.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
-
-    // Check that we have removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderConstClassFromCalleeTest.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderConstClassFromCalleeTest.java
index da90e4a..a6556a2 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderConstClassFromCalleeTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderConstClassFromCalleeTest.java
@@ -5,41 +5,30 @@
 
 import static junit.framework.TestCase.assertEquals;
 
-import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.StringUtils;
 import java.util.ServiceLoader;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
 public class ServiceLoaderConstClassFromCalleeTest extends ServiceLoaderTestBase {
 
-  @Parameter(0)
-  public TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
+  public ServiceLoaderConstClassFromCalleeTest(TestParameters parameters) {
+    super(parameters);
+  }
+
   @Test
   public void test() throws Exception {
-    testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
+    serviceLoaderTest(Service.class, ServiceImpl.class, ServiceImpl2.class)
         .addKeepMainRule(Main.class)
-        .setMinApi(parameters)
-        .addDataEntryResources(
-            DataEntryResource.fromBytes(
-                StringUtils.lines(ServiceImpl.class.getTypeName(), ServiceImpl2.class.getTypeName())
-                    .getBytes(),
-                "META-INF/services/" + Service.class.getTypeName(),
-                Origin.unknown()))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("Hello, world!")
         // Check that the call to ServiceLoader.load is removed.
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderMultipleCallsSameMethodTest.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderMultipleCallsSameMethodTest.java
index 149fd33..111badc 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderMultipleCallsSameMethodTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderMultipleCallsSameMethodTest.java
@@ -5,31 +5,24 @@
 package com.android.tools.r8.optimize.serviceloader;
 
 import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
 import static junit.framework.TestCase.assertTrue;
 import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import java.io.IOException;
-import java.nio.file.Path;
 import java.util.ServiceLoader;
 import java.util.concurrent.ExecutionException;
-import java.util.zip.ZipFile;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
 public class ServiceLoaderMultipleCallsSameMethodTest extends ServiceLoaderTestBase {
-
-  private final TestParameters parameters;
   private final String EXPECTED_OUTPUT = StringUtils.lines("Hello World!", "Hello World!");
 
   public interface Service {
@@ -45,13 +38,6 @@
     }
   }
 
-  public static class ServiceImpl2 implements Service {
-
-    @Override
-    public void print() {
-      System.out.println("Hello World 2!");
-    }
-  }
 
   public static class MainRunner {
 
@@ -77,31 +63,22 @@
   }
 
   public ServiceLoaderMultipleCallsSameMethodTest(TestParameters parameters) {
-    this.parameters = parameters;
+    super(parameters);
   }
 
   @Test
   public void testRewritings() throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    testForR8(parameters.getBackend())
-        .addInnerClasses(ServiceLoaderMultipleCallsSameMethodTest.class)
+    serviceLoaderTest(Service.class, ServiceImpl.class)
         .addKeepMainRule(MainRunner.class)
-        .setMinApi(parameters)
         .enableInliningAnnotations()
-        .addDataEntryResources(
-            DataEntryResource.fromBytes(
-                StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
-                "META-INF/services/" + Service.class.getTypeName(),
-                Origin.unknown()))
         .compile()
-        .writeToZip(path)
         .run(parameters.getRuntime(), MainRunner.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT)
-        // Check that we have actually rewritten the calls to ServiceLoader.load.
-        .inspect(this::verifyNoServiceLoaderLoads)
-        .inspect(this::verifyNoClassLoaders)
         .inspect(
             inspector -> {
+              verifyNoServiceLoaderLoads(inspector);
+              verifyNoClassLoaders(inspector);
+              verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class);
               // Check the synthesize service loader method is a single shared method.
               // Due to minification we just check there is only a single synthetic class with a
               // single static method.
@@ -115,9 +92,5 @@
                 }
               }
             });
-
-    // Check that we have removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderMultipleCallsTest.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderMultipleCallsTest.java
index c688bd4..5b2d452 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderMultipleCallsTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderMultipleCallsTest.java
@@ -5,31 +5,24 @@
 package com.android.tools.r8.optimize.serviceloader;
 
 import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
 import static junit.framework.TestCase.assertTrue;
 import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import java.io.IOException;
-import java.nio.file.Path;
 import java.util.ServiceLoader;
 import java.util.concurrent.ExecutionException;
-import java.util.zip.ZipFile;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
 public class ServiceLoaderMultipleCallsTest extends ServiceLoaderTestBase {
-
-  private final TestParameters parameters;
   private final String EXPECTED_OUTPUT = StringUtils.lines("Hello World!", "Hello World!");
 
   public interface Service {
@@ -45,14 +38,6 @@
     }
   }
 
-  public static class ServiceImpl2 implements Service {
-
-    @Override
-    public void print() {
-      System.out.println("Hello World 2!");
-    }
-  }
-
   public static class MainRunner {
 
     public static void main(String[] args) {
@@ -81,31 +66,22 @@
   }
 
   public ServiceLoaderMultipleCallsTest(TestParameters parameters) {
-    this.parameters = parameters;
+    super(parameters);
   }
 
   @Test
   public void testRewritings() throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    testForR8(parameters.getBackend())
-        .addInnerClasses(ServiceLoaderMultipleCallsTest.class)
+    serviceLoaderTest(Service.class, ServiceImpl.class)
         .addKeepMainRule(MainRunner.class)
-        .setMinApi(parameters)
         .enableInliningAnnotations()
-        .addDataEntryResources(
-            DataEntryResource.fromBytes(
-                StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
-                "META-INF/services/" + Service.class.getTypeName(),
-                Origin.unknown()))
         .compile()
-        .writeToZip(path)
         .run(parameters.getRuntime(), MainRunner.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT)
         .inspect(
             inspector -> {
               // Check that we have actually rewritten the calls to ServiceLoader.load.
               assertEquals(0, getServiceLoaderLoads(inspector));
-              // Check the synthesize service loader method is a single shared method.
+              // Check the synthesized service loader method is a single shared method.
               // Due to minification we just check there is only a single synthetic class with a
               // single static method.
               boolean found = false;
@@ -118,9 +94,5 @@
                 }
               }
             });
-
-    // Check that we have removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingLineSeparatorTest.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingLineSeparatorTest.java
index 7610575..c142442 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingLineSeparatorTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingLineSeparatorTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.optimize.serviceloader;
 
 import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DataEntryResource;
@@ -13,10 +12,8 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
-import java.nio.file.Path;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
-import java.util.zip.ZipFile;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -24,8 +21,6 @@
 
 @RunWith(Parameterized.class)
 public class ServiceLoaderRewritingLineSeparatorTest extends ServiceLoaderTestBase {
-
-  private final TestParameters parameters;
   private final Separator lineSeparator;
 
   private final String EXPECTED_OUTPUT =
@@ -55,18 +50,16 @@
   }
 
   public ServiceLoaderRewritingLineSeparatorTest(TestParameters parameters, Separator separator) {
-    this.parameters = parameters;
+    super(parameters);
     this.lineSeparator = separator;
   }
 
   @Test
   public void testRewritingWithMultipleWithLineSeparator()
       throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    testForR8(parameters.getBackend())
+    serviceLoaderTest(null)
         .addInnerClasses(ServiceLoaderRewritingTest.class)
         .addKeepMainRule(ServiceLoaderRewritingTest.MainRunner.class)
-        .setMinApi(parameters)
         .addDataEntryResources(
             DataEntryResource.fromBytes(
                 StringUtils.join(
@@ -77,14 +70,17 @@
                 "META-INF/services/" + ServiceLoaderRewritingTest.Service.class.getTypeName(),
                 Origin.unknown()))
         .compile()
-        .writeToZip(path)
         .run(parameters.getRuntime(), ServiceLoaderRewritingTest.MainRunner.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT + StringUtils.lines("Hello World 2!"))
         // Check that we have actually rewritten the calls to ServiceLoader.load.
-        .inspect(inspector -> assertEquals(0, getServiceLoaderLoads(inspector)));
-
-    // Check that we have removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    assertNull(zip.getEntry("META-INF/services"));
+        .inspect(
+            inspector -> {
+              assertEquals(0, getServiceLoaderLoads(inspector));
+              verifyServiceMetaInf(
+                  inspector,
+                  ServiceLoaderRewritingTest.Service.class,
+                  ServiceLoaderRewritingTest.ServiceImpl.class,
+                  ServiceLoaderRewritingTest.ServiceImpl2.class);
+            });
   }
 }
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingTest.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingTest.java
index 1483561..c9fc05e 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingTest.java
@@ -4,30 +4,22 @@
 
 package com.android.tools.r8.optimize.serviceloader;
 
-import static com.android.tools.r8.TestBase.getTestParameters;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
-import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.DiagnosticsMatcher;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ir.optimize.ServiceLoaderRewriterDiagnostic;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.io.IOException;
-import java.nio.file.Path;
+import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.ServiceLoader;
 import java.util.concurrent.ExecutionException;
-import java.util.zip.ZipFile;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -35,7 +27,6 @@
 @RunWith(Parameterized.class)
 public class ServiceLoaderRewritingTest extends ServiceLoaderTestBase {
 
-  private final TestParameters parameters;
   private final String EXPECTED_OUTPUT =
       StringUtils.lines("Hello World!", "Hello World!", "Hello World!");
 
@@ -156,202 +147,124 @@
   }
 
   public ServiceLoaderRewritingTest(TestParameters parameters) {
-    this.parameters = parameters;
+    super(parameters);
+  }
+
+  @Test
+  public void testRewritingWithNoImpls()
+      throws IOException, CompilationFailedException, ExecutionException {
+    serviceLoaderTest(null)
+        .addKeepMainRule(MainRunner.class)
+        .compile()
+        .assertAllInfosMatch(
+            DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
+        .run(parameters.getRuntime(), MainRunner.class)
+        .assertFailureWithErrorThatThrows(NoSuchElementException.class)
+        .inspectFailure(inspector -> assertEquals(1, getServiceLoaderLoads(inspector)));
   }
 
   @Test
   public void testRewritings() throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    testForR8(parameters.getBackend())
-        .addInnerClasses(ServiceLoaderRewritingTest.class)
+    serviceLoaderTest(Service.class, ServiceImpl.class)
         .addKeepMainRule(MainRunner.class)
-        .setMinApi(parameters)
-        .addDataEntryResources(
-            DataEntryResource.fromBytes(
-                StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
-                "META-INF/services/" + Service.class.getTypeName(),
-                Origin.unknown()))
         .compile()
-        .writeToZip(path)
         .run(parameters.getRuntime(), MainRunner.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT)
-        // Check that we have actually rewritten the calls to ServiceLoader.load.
-        .inspect(inspector -> assertEquals(0, getServiceLoaderLoads(inspector)));
-
-    // Check that we have removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
+        .inspect(
+            inspector -> {
+              List<String> lines = dataResourceConsumer.get("META-INF/services/com.example.Foo");
+              assertEquals(0, getServiceLoaderLoads(inspector));
+              verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class);
+            });
   }
 
   @Test
   public void testRewritingWithMultiple()
       throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    testForR8(parameters.getBackend())
-        .addInnerClasses(ServiceLoaderRewritingTest.class)
+    serviceLoaderTest(Service.class, ServiceImpl.class, ServiceImpl2.class)
         .addKeepMainRule(MainRunner.class)
-        .setMinApi(parameters)
-        .addDataEntryResources(
-            DataEntryResource.fromBytes(
-                StringUtils.lines(ServiceImpl.class.getTypeName(), ServiceImpl2.class.getTypeName())
-                    .getBytes(),
-                "META-INF/services/" + Service.class.getTypeName(),
-                Origin.unknown()))
         .compile()
-        .writeToZip(path)
         .run(parameters.getRuntime(), MainRunner.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT + StringUtils.lines("Hello World 2!"))
-        // Check that we have actually rewritten the calls to ServiceLoader.load.
-        .inspect(inspector -> assertEquals(0, getServiceLoaderLoads(inspector)));
-
-    // Check that we have removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
+        .inspect(
+            inspector -> {
+              assertEquals(0, getServiceLoaderLoads(inspector));
+              verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class, ServiceImpl2.class);
+            });
   }
 
   @Test
   public void testRewritingsWithCatchHandlers()
       throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    testForR8(parameters.getBackend())
-        .addInnerClasses(ServiceLoaderRewritingTest.class)
+    serviceLoaderTest(Service.class, ServiceImpl.class, ServiceImpl2.class)
         .addKeepMainRule(MainWithTryCatchRunner.class)
-        .setMinApi(parameters)
-        .addDataEntryResources(
-            DataEntryResource.fromBytes(
-                StringUtils.lines(ServiceImpl.class.getTypeName(), ServiceImpl2.class.getTypeName())
-                    .getBytes(),
-                "META-INF/services/" + Service.class.getTypeName(),
-                Origin.unknown()))
         .compile()
-        .writeToZip(path)
         .run(parameters.getRuntime(), MainWithTryCatchRunner.class)
         .assertSuccessWithOutput(StringUtils.lines("Hello World!"))
-        // Check that we have actually rewritten the calls to ServiceLoader.load.
-        .inspect(inspector -> assertEquals(0, getServiceLoaderLoads(inspector)));
-
-    // Check that we have removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
+        .inspect(
+            inspector -> {
+              assertEquals(0, getServiceLoaderLoads(inspector));
+              verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class, ServiceImpl2.class);
+            });
   }
 
   @Test
   public void testDoNoRewrite() throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    CodeInspector inspector =
-        testForR8(parameters.getBackend())
-            .addInnerClasses(ServiceLoaderRewritingTest.class)
-            .addKeepMainRule(OtherRunner.class)
-            .setMinApi(parameters)
-            .addKeepRules(
-                "-whyareyounotinlining class "
-                    + ServiceLoader.class.getTypeName()
-                    + " { *** load(...); }")
-            .enableExperimentalWhyAreYouNotInlining()
-            .addDataEntryResources(
-                DataEntryResource.fromBytes(
-                    StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
-                    "META-INF/services/" + Service.class.getTypeName(),
-                    Origin.unknown()))
-            .allowDiagnosticInfoMessages()
-            .compile()
-            .assertAllInfosMatch(
-                DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
-            .assertAtLeastOneInfoMessage()
-            .writeToZip(path)
-            .run(parameters.getRuntime(), OtherRunner.class)
-            .assertSuccessWithOutput(EXPECTED_OUTPUT)
-            .inspector();
-
-    // Check that we have not rewritten the calls to ServiceLoader.load.
-    assertEquals(3, getServiceLoaderLoads(inspector));
-
-    // Check that we have not removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    ClassSubject serviceImpl = inspector.clazz(ServiceImpl.class);
-    assertTrue(serviceImpl.isPresent());
-    assertNotNull(zip.getEntry("META-INF/services/" + serviceImpl.getFinalName()));
+    serviceLoaderTest(Service.class, ServiceImpl.class)
+        .addKeepMainRule(OtherRunner.class)
+        .allowDiagnosticInfoMessages()
+        .compile()
+        .assertAllInfosMatch(
+            DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
+        .assertAtLeastOneInfoMessage()
+        .run(parameters.getRuntime(), OtherRunner.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT)
+        .inspect(
+            inspector -> {
+              assertEquals(3, getServiceLoaderLoads(inspector));
+              verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class);
+            });
   }
 
   @Test
   public void testDoNoRewriteWhenEscaping()
       throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    CodeInspector inspector =
-        testForR8(parameters.getBackend())
-            .addInnerClasses(ServiceLoaderRewritingTest.class)
-            .addKeepMainRule(EscapingRunner.class)
-            .enableInliningAnnotations()
-            .setMinApi(parameters)
-            .addKeepRules(
-                "-whyareyounotinlining class "
-                    + ServiceLoader.class.getTypeName()
-                    + " { *** load(...); }")
-            .enableExperimentalWhyAreYouNotInlining()
-            .addDontObfuscate()
-            .addDataEntryResources(
-                DataEntryResource.fromBytes(
-                    StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
-                    "META-INF/services/" + Service.class.getTypeName(),
-                    Origin.unknown()))
-            .allowDiagnosticInfoMessages()
-            .compile()
-            .assertAllInfosMatch(
-                DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
-            .assertAtLeastOneInfoMessage()
-            .writeToZip(path)
-            .run(parameters.getRuntime(), EscapingRunner.class)
-            .assertSuccessWithOutput(EXPECTED_OUTPUT)
-            .inspector();
-
-    // Check that we have not rewritten the calls to ServiceLoader.load.
-    assertEquals(3, getServiceLoaderLoads(inspector));
-
-    // Check that we have not removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    ClassSubject serviceImpl = inspector.clazz(ServiceImpl.class);
-    assertTrue(serviceImpl.isPresent());
-    assertNotNull(zip.getEntry("META-INF/services/" + serviceImpl.getFinalName()));
+    serviceLoaderTest(Service.class, ServiceImpl.class)
+        .addKeepMainRule(EscapingRunner.class)
+        .enableInliningAnnotations()
+        .addDontObfuscate()
+        .allowDiagnosticInfoMessages()
+        .compile()
+        .assertAllInfosMatch(
+            DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
+        .assertAtLeastOneInfoMessage()
+        .run(parameters.getRuntime(), EscapingRunner.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT)
+        .inspect(
+            inspector -> {
+              assertEquals(3, getServiceLoaderLoads(inspector));
+              verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class);
+            });
   }
 
   @Test
   public void testDoNoRewriteWhenClassLoaderIsPhi()
       throws IOException, CompilationFailedException, ExecutionException {
-    Path path = temp.newFile("out.zip").toPath();
-    CodeInspector inspector =
-        testForR8(parameters.getBackend())
-            .addInnerClasses(ServiceLoaderRewritingTest.class)
-            .addKeepMainRule(LoadWhereClassLoaderIsPhi.class)
-            .enableInliningAnnotations()
-            .setMinApi(parameters)
-            .addKeepRules(
-                "-whyareyounotinlining class "
-                    + ServiceLoader.class.getTypeName()
-                    + " { *** load(...); }")
-            .enableExperimentalWhyAreYouNotInlining()
-            .addDataEntryResources(
-                DataEntryResource.fromBytes(
-                    StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
-                    "META-INF/services/" + Service.class.getTypeName(),
-                    Origin.unknown()))
-            .allowDiagnosticInfoMessages()
-            .compile()
-            .assertAllInfosMatch(
-                DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
-            .assertAtLeastOneInfoMessage()
-            .writeToZip(path)
-            .run(parameters.getRuntime(), LoadWhereClassLoaderIsPhi.class)
-            .assertSuccessWithOutputLines("Hello World!")
-            .inspector();
-
-    // Check that we have not rewritten the calls to ServiceLoader.load.
-    assertEquals(1, getServiceLoaderLoads(inspector));
-
-    // Check that we have not removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    ClassSubject serviceImpl = inspector.clazz(ServiceImpl.class);
-    assertTrue(serviceImpl.isPresent());
-    assertNotNull(zip.getEntry("META-INF/services/" + serviceImpl.getFinalName()));
+    serviceLoaderTest(Service.class, ServiceImpl.class)
+        .addKeepMainRule(LoadWhereClassLoaderIsPhi.class)
+        .enableInliningAnnotations()
+        .allowDiagnosticInfoMessages()
+        .compile()
+        .assertAllInfosMatch(
+            DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
+        .assertAtLeastOneInfoMessage()
+        .run(parameters.getRuntime(), LoadWhereClassLoaderIsPhi.class)
+        .assertSuccessWithOutputLines("Hello World!")
+        .inspect(
+            inspector -> {
+              assertEquals(1, getServiceLoaderLoads(inspector));
+              verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class);
+            });
   }
 
   @Test
@@ -362,40 +275,20 @@
     assumeTrue(
         parameters.getRuntime().isCf()
             || !parameters.getRuntime().asDex().getVm().getVersion().equals(Version.V7_0_0));
-    Path path = temp.newFile("out.zip").toPath();
-    CodeInspector inspector =
-        testForR8(parameters.getBackend())
-            .addInnerClasses(ServiceLoaderRewritingTest.class)
-            .addKeepMainRule(MainRunner.class)
-            .addKeepClassRules(Service.class)
-            .setMinApi(parameters)
-            .addKeepRules(
-                "-whyareyounotinlining class "
-                    + ServiceLoader.class.getTypeName()
-                    + " { *** load(...); }")
-            .enableExperimentalWhyAreYouNotInlining()
-            .addDataEntryResources(
-                DataEntryResource.fromBytes(
-                    StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
-                    "META-INF/services/" + Service.class.getTypeName(),
-                    Origin.unknown()))
-            .allowDiagnosticInfoMessages()
-            .compile()
-            .assertAllInfosMatch(
-                DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
-            .assertAtLeastOneInfoMessage()
-            .writeToZip(path)
-            .run(parameters.getRuntime(), MainRunner.class)
-            .assertSuccessWithOutput(EXPECTED_OUTPUT)
-            .inspector();
-
-    // Check that we have not rewritten the calls to ServiceLoader.load.
-    assertEquals(3, getServiceLoaderLoads(inspector));
-
-    // Check that we have not removed the service configuration from META-INF/services.
-    ZipFile zip = new ZipFile(path.toFile());
-    ClassSubject service = inspector.clazz(Service.class);
-    assertTrue(service.isPresent());
-    assertNotNull(zip.getEntry("META-INF/services/" + service.getFinalName()));
+    serviceLoaderTest(Service.class, ServiceImpl.class)
+        .addKeepMainRule(MainRunner.class)
+        .addKeepClassRules(Service.class)
+        .allowDiagnosticInfoMessages()
+        .compile()
+        .assertAllInfosMatch(
+            DiagnosticsMatcher.diagnosticType(ServiceLoaderRewriterDiagnostic.class))
+        .assertAtLeastOneInfoMessage()
+        .run(parameters.getRuntime(), MainRunner.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT)
+        .inspect(
+            inspector -> {
+              assertEquals(3, getServiceLoaderLoads(inspector));
+              verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class);
+            });
   }
 }
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderTestBase.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderTestBase.java
index 21276a2..cf1fc6f 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderTestBase.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderTestBase.java
@@ -4,38 +4,43 @@
 package com.android.tools.r8.optimize.serviceloader;
 
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
-import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.stream.Collectors;
 
 public class ServiceLoaderTestBase extends TestBase {
+  protected final TestParameters parameters;
+  protected DataResourceConsumerForTesting dataResourceConsumer;
+
+  public ServiceLoaderTestBase(TestParameters parameters) {
+    this.parameters = parameters;
+  }
 
   public static long getServiceLoaderLoads(CodeInspector inspector) {
-    return inspector.allClasses().stream()
-        .mapToLong(ServiceLoaderTestBase::getServiceLoaderLoads)
-        .reduce(0, Long::sum);
-  }
-
-  public static long getServiceLoaderLoads(CodeInspector inspector, Class<?> clazz) {
-    return getServiceLoaderLoads(inspector.clazz(clazz));
-  }
-
-  public static long getServiceLoaderLoads(ClassSubject classSubject) {
-    assertTrue(classSubject.isPresent());
-    return classSubject.allMethods(MethodSubject::hasCode).stream()
-        .mapToLong(
-            method ->
-                method
-                    .streamInstructions()
-                    .filter(ServiceLoaderTestBase::isServiceLoaderLoad)
-                    .count())
-        .sum();
+    return inspector
+        .streamInstructions()
+        .filter(ServiceLoaderTestBase::isServiceLoaderLoad)
+        .count();
   }
 
   private static boolean isServiceLoaderLoad(InstructionSubject instruction) {
@@ -43,22 +48,93 @@
         && instruction.getMethod().qualifiedName().contains("ServiceLoader.load");
   }
 
-  public void verifyNoClassLoaders(CodeInspector inspector) {
-    inspector.allClasses().forEach(this::verifyNoClassLoaders);
+  public static void verifyNoClassLoaders(CodeInspector inspector) {
+    inspector.allClasses().forEach(ServiceLoaderTestBase::verifyNoClassLoaders);
   }
 
-  public void verifyNoClassLoaders(ClassSubject classSubject) {
+  public static void verifyNoClassLoaders(ClassSubject classSubject) {
     assertTrue(classSubject.isPresent());
     classSubject.forAllMethods(
         method -> assertThat(method, not(invokesMethodWithName("getClassLoader"))));
   }
 
-  public void verifyNoServiceLoaderLoads(CodeInspector inspector) {
-    inspector.allClasses().forEach(this::verifyNoServiceLoaderLoads);
+  public static void verifyNoServiceLoaderLoads(CodeInspector inspector) {
+    inspector.allClasses().forEach(ServiceLoaderTestBase::verifyNoServiceLoaderLoads);
   }
 
-  public void verifyNoServiceLoaderLoads(ClassSubject classSubject) {
+  public static void verifyNoServiceLoaderLoads(ClassSubject classSubject) {
     assertTrue(classSubject.isPresent());
     classSubject.forAllMethods(method -> assertThat(method, not(invokesMethodWithName("load"))));
   }
+
+  public Map<String, List<String>> getServiceMappings() {
+    return dataResourceConsumer.getAll().entrySet().stream()
+        .filter(e -> e.getKey().startsWith(AppServices.SERVICE_DIRECTORY_NAME))
+        .collect(
+            Collectors.toMap(
+                e -> e.getKey().substring(AppServices.SERVICE_DIRECTORY_NAME.length()),
+                e -> e.getValue()));
+  }
+
+  public void verifyServiceMetaInf(
+      CodeInspector inspector, Class<?> serviceClass, Class<?> serviceImplClass) {
+    // Account for renaming, and for the impl to be merged with the interface.
+    String finalServiceName = inspector.clazz(serviceClass).getFinalName();
+    String finalImplName = inspector.clazz(serviceImplClass).getFinalName();
+    if (finalServiceName == null) {
+      finalServiceName = finalImplName;
+    }
+    Map<String, List<String>> actual = getServiceMappings();
+    Map<String, List<String>> expected =
+        ImmutableMap.of(finalServiceName, Collections.singletonList(finalImplName));
+    assertEquals(expected, actual);
+  }
+
+  public void verifyServiceMetaInf(
+      CodeInspector inspector,
+      Class<?> serviceClass,
+      Class<?> serviceImplClass1,
+      Class<?> serviceImplClass2) {
+    // Account for renaming. No class merging should happen.
+    String finalServiceName = inspector.clazz(serviceClass).getFinalName();
+    String finalImplName1 = inspector.clazz(serviceImplClass1).getFinalName();
+    String finalImplName2 = inspector.clazz(serviceImplClass2).getFinalName();
+    Map<String, List<String>> actual = getServiceMappings();
+    Map<String, List<String>> expected =
+        ImmutableMap.of(finalServiceName, Arrays.asList(finalImplName1, finalImplName2));
+    assertEquals(expected, actual);
+  }
+
+  protected R8FullTestBuilder serviceLoaderTest(Class<?> serviceClass, Class<?>... implClasses)
+      throws IOException {
+    return serviceLoaderTestNoClasses(serviceClass, implClasses).addInnerClasses(getClass());
+  }
+
+  protected R8FullTestBuilder serviceLoaderTestNoClasses(
+      Class<?> serviceClass, Class<?>... implClasses) throws IOException {
+    R8FullTestBuilder ret =
+        testForR8(parameters.getBackend())
+            .setMinApi(parameters)
+            .addOptionsModification(
+                o -> {
+                  dataResourceConsumer = new DataResourceConsumerForTesting(o.dataResourceConsumer);
+                  o.dataResourceConsumer = dataResourceConsumer;
+                })
+            // Enables ServiceLoader optimization failure diagnostics.
+            .enableExperimentalWhyAreYouNotInlining()
+            .addKeepRules(
+                "-whyareyounotinlining class java.util.ServiceLoader { *** load(...); }");
+    if (implClasses.length > 0) {
+      String implLines =
+          Arrays.stream(implClasses)
+              .map(c -> c.getTypeName() + "\n")
+              .collect(Collectors.joining(""));
+      ret.addDataEntryResources(
+          DataEntryResource.fromBytes(
+              implLines.getBytes(),
+              AppServices.SERVICE_DIRECTORY_NAME + serviceClass.getTypeName(),
+              Origin.unknown()));
+    }
+    return ret;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/DataResourceConsumerForTesting.java b/src/test/java/com/android/tools/r8/utils/DataResourceConsumerForTesting.java
index 77274e6..dc2dfe8 100644
--- a/src/test/java/com/android/tools/r8/utils/DataResourceConsumerForTesting.java
+++ b/src/test/java/com/android/tools/r8/utils/DataResourceConsumerForTesting.java
@@ -54,6 +54,10 @@
   @Override
   public void finished(DiagnosticsHandler handler) {}
 
+  public Map<String, ImmutableList<String>> getAll() {
+    return resources;
+  }
+
   public ImmutableList<String> get(String name) {
     return resources.get(name);
   }
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 74bdc47..7c06d42 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -67,6 +67,7 @@
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class CodeInspector {
 
@@ -401,6 +402,12 @@
     return builder.build();
   }
 
+  public Stream<InstructionSubject> streamInstructions() {
+    return allClasses().stream()
+        .flatMap(cls -> cls.allMethods(MethodSubject::hasCode).stream())
+        .flatMap(MethodSubject::streamInstructions);
+  }
+
   public FieldSubject field(Field field) {
     return field(Reference.fieldFromField(field));
   }