Update BaseHostJunit4 to decide the instrumentation dynamically

Instead of forcing AJUR runner, get it dynamically from the
pm instrumentation.

cherry pick from go/ag/4628373
Test: cts-tradefed run cts -m CtsBackupHostTestCases
Bug: 129151011
Merged-In: Idadce9f0e4ad3e67db758735b3cb04e72b4976c9

Change-Id: Ice9a44e1b378ff8f1167ad5c81efee6ec475cd98
diff --git a/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4Test.java b/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4Test.java
index e63c668..4cd3c69 100644
--- a/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4Test.java
+++ b/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4Test.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.annotations.VisibleForTesting;
-import com.android.ddmlib.IDevice;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestResult.TestStatus;
@@ -40,6 +39,8 @@
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
 import com.android.tradefed.testtype.IInvocationContextReceiver;
+import com.android.tradefed.util.ListInstrumentationParser;
+import com.android.tradefed.util.ListInstrumentationParser.InstrumentationTarget;
 
 import org.junit.After;
 import org.junit.Assume;
@@ -58,7 +59,6 @@
 public abstract class BaseHostJUnit4Test
         implements IAbiReceiver, IBuildReceiver, IDeviceTest, IInvocationContextReceiver {
 
-    static final String AJUR_RUNNER = "android.support.test.runner.AndroidJUnitRunner";
     static final long DEFAULT_TEST_TIMEOUT_MS = 10 * 60 * 1000L;
     private static final long DEFAULT_MAX_TIMEOUT_TO_OUTPUT_MS = 10 * 60 * 1000L; //10min
 
@@ -227,7 +227,7 @@
             throws DeviceNotAvailableException {
         return runDeviceTests(
                 getDevice(),
-                AJUR_RUNNER,
+                null,
                 pkgName,
                 testClassName,
                 null,
@@ -254,7 +254,7 @@
             throws DeviceNotAvailableException {
         return runDeviceTests(
                 getDevice(),
-                AJUR_RUNNER,
+                null,
                 pkgName,
                 testClassName,
                 null,
@@ -322,7 +322,7 @@
             throws DeviceNotAvailableException {
         return runDeviceTests(
                 device,
-                AJUR_RUNNER,
+                null,
                 pkgName,
                 testClassName,
                 testMethodName,
@@ -354,7 +354,7 @@
             throws DeviceNotAvailableException {
         return runDeviceTests(
                 device,
-                AJUR_RUNNER,
+                null,
                 pkgName,
                 testClassName,
                 testMethodName,
@@ -388,7 +388,7 @@
             throws DeviceNotAvailableException {
         return runDeviceTests(
                 device,
-                AJUR_RUNNER,
+                null,
                 pkgName,
                 testClassName,
                 testMethodName,
@@ -424,7 +424,7 @@
             throws DeviceNotAvailableException {
         return runDeviceTests(
                 device,
-                AJUR_RUNNER,
+                null,
                 pkgName,
                 testClassName,
                 testMethodName,
@@ -555,7 +555,7 @@
             Long maxInstrumentationTimeoutMs,
             boolean isHiddenApiCheckDisabled)
             throws DeviceNotAvailableException {
-        RemoteAndroidTestRunner testRunner = createTestRunner(pkgName, runner, device.getIDevice());
+        RemoteAndroidTestRunner testRunner = createTestRunner(pkgName, runner, device);
         String runOptions = "";
         // hidden-api-checks flag only exists in P and after.
         if (isHiddenApiCheckDisabled && (device.getApiLevel() >= 28)) {
@@ -599,8 +599,27 @@
 
     @VisibleForTesting
     RemoteAndroidTestRunner createTestRunner(
-            String packageName, String runnerName, IDevice device) {
-        return new DefaultRemoteAndroidTestRunner(packageName, runnerName, device);
+            String packageName, String runnerName, ITestDevice device)
+            throws DeviceNotAvailableException {
+        if (runnerName == null) {
+            ListInstrumentationParser parser = getListInstrumentationParser();
+            device.executeShellCommand("pm list instrumentation", parser);
+            for (InstrumentationTarget target : parser.getInstrumentationTargets()) {
+                if (packageName.equals(target.packageName)) {
+                    runnerName = target.runnerName;
+                }
+            }
+        }
+        // If the runner name is still null
+        if (runnerName == null) {
+            throw new RuntimeException("No runner was defined and couldn't dynamically find one.");
+        }
+        return new DefaultRemoteAndroidTestRunner(packageName, runnerName, device.getIDevice());
+    }
+
+    @VisibleForTesting
+    ListInstrumentationParser getListInstrumentationParser() {
+        return new ListInstrumentationParser();
     }
 
     @VisibleForTesting
diff --git a/src/com/android/tradefed/testtype/junit4/DeviceTestRunOptions.java b/src/com/android/tradefed/testtype/junit4/DeviceTestRunOptions.java
index 6808fc8..1060ffd 100644
--- a/src/com/android/tradefed/testtype/junit4/DeviceTestRunOptions.java
+++ b/src/com/android/tradefed/testtype/junit4/DeviceTestRunOptions.java
@@ -20,7 +20,7 @@
 /** A builder class for options related to running device tests through BaseHostJUnit4Test. */
 public class DeviceTestRunOptions {
     private ITestDevice mDevice; // optional
-    private String mRunner = BaseHostJUnit4Test.AJUR_RUNNER; // optional
+    private String mRunner = null; // optional
     private final String mPackageName; // required
 
     private String mTestClassName; // optional
diff --git a/tests/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4TestTest.java b/tests/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4TestTest.java
index c5c2ce6..423c4c1 100644
--- a/tests/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4TestTest.java
+++ b/tests/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4TestTest.java
@@ -18,7 +18,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.android.ddmlib.IDevice;
 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.tradefed.build.IBuildInfo;
@@ -39,6 +38,7 @@
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.HostTest;
 import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.ListInstrumentationParser;
 
 import org.easymock.EasyMock;
 import org.junit.Assert;
@@ -76,6 +76,18 @@
             listener.testRunEnded(500l, new HashMap<String, Metric>());
             return listener;
         }
+
+        @Override
+        ListInstrumentationParser getListInstrumentationParser() {
+            ListInstrumentationParser parser = new ListInstrumentationParser();
+            parser.processNewLines(
+                    new String[] {
+                        "instrumentation:com.package/"
+                                + "android.support.test.runner.AndroidJUnitRunner "
+                                + "(target=com.example2)"
+                    });
+            return parser;
+        }
     }
 
     /**
@@ -83,7 +95,7 @@
      * run.
      */
     @RunWith(DeviceJUnit4ClassRunner.class)
-    public static class FailureHostJUnit4Test extends BaseHostJUnit4Test {
+    public static class FailureHostJUnit4Test extends TestableHostJUnit4Test {
         @Test
         public void testOne() {
             Assert.assertNotNull(getDevice());
@@ -153,6 +165,8 @@
         test.setDevice(mMockDevice);
         test.setBuild(mMockBuild);
         test.setInvocationContext(mMockContext);
+        mMockDevice.executeShellCommand(
+                EasyMock.eq("pm list instrumentation"), EasyMock.anyObject());
         EasyMock.expect(mMockDevice.getIDevice()).andReturn(new StubDevice("serial"));
         EasyMock.expect(
                         mMockDevice.runInstrumentationTests(
@@ -177,7 +191,7 @@
                 new TestableHostJUnit4Test() {
                     @Override
                     RemoteAndroidTestRunner createTestRunner(
-                            String packageName, String runnerName, IDevice device) {
+                            String packageName, String runnerName, ITestDevice device) {
                         return runner;
                     }
                 };
@@ -185,7 +199,6 @@
         test.setBuild(mMockBuild);
         test.setInvocationContext(mMockContext);
         test.setAbi(new Abi("arm", "32"));
-        EasyMock.expect(mMockDevice.getIDevice()).andReturn(new StubDevice("serial"));
         EasyMock.expect(
                         mMockDevice.runInstrumentationTests(
                                 (IRemoteAndroidTestRunner) EasyMock.anyObject(),
@@ -213,6 +226,8 @@
         test.setDevice(mMockDevice);
         test.setBuild(mMockBuild);
         test.setInvocationContext(mMockContext);
+        mMockDevice.executeShellCommand(
+                EasyMock.eq("pm list instrumentation"), EasyMock.anyObject());
         EasyMock.expect(mMockDevice.getIDevice()).andReturn(new StubDevice("serial"));
         EasyMock.expect(
                         mMockDevice.runInstrumentationTestsAsUser(
@@ -222,7 +237,7 @@
                 .andReturn(true);
         EasyMock.replay(mMockBuild, mMockDevice);
         try {
-            test.runDeviceTests("package", "class", 0, null);
+            test.runDeviceTests("com.package", "class", 0, null);
         } catch (AssumptionViolatedException e) {
             // Ensure that the Assume logic in the test does not make a false pass for the unit test
             fail("Should not have thrown an Assume exception.");
@@ -240,6 +255,8 @@
         test.setDevice(mMockDevice);
         test.setBuild(mMockBuild);
         test.setInvocationContext(mMockContext);
+        mMockDevice.executeShellCommand(
+                EasyMock.eq("pm list instrumentation"), EasyMock.anyObject());
         EasyMock.expect(mMockDevice.getIDevice()).andReturn(new StubDevice("serial"));
         EasyMock.expect(
                         mMockDevice.runInstrumentationTests(
@@ -267,6 +284,8 @@
         test.setDevice(mMockDevice);
         test.setBuild(mMockBuild);
         test.setInvocationContext(mMockContext);
+        mMockDevice.executeShellCommand(
+                EasyMock.eq("pm list instrumentation"), EasyMock.anyObject());
         EasyMock.expect(mMockDevice.getIDevice()).andReturn(new StubDevice("serial"));
         EasyMock.expect(
                         mMockDevice.runInstrumentationTests(
@@ -275,7 +294,7 @@
                 .andReturn(true);
         EasyMock.replay(mMockBuild, mMockDevice);
         try {
-            test.runDeviceTests("com.package", "testClass");
+            test.runDeviceTests("com.package", "class");
         } catch (AssumptionViolatedException e) {
             // Ensure that the Assume logic in the test does not make a false pass for the unit test
             fail("Should not have thrown an Assume exception.");