Commit bf137c2a authored by Eric Stevenson's avatar Eric Stevenson Committed by Commit Bot

Android: Fix "failing to list tests" on KitKat.

https://chromium-review.googlesource.com/c/chromium/src/+/1319335
caused some tests to be moved to secondary dex files which are ignored
by TestRequestBuilder KitKat.

This CL adds support by manually listing tests the same way
incremental install does.

Bug: 903820
Change-Id: I1cb874c4bf570d482bc28668978234538629e261
Reviewed-on: https://chromium-review.googlesource.com/c/1332607Reviewed-by: default avatarJohn Budorick <jbudorick@chromium.org>
Commit-Queue: Eric Stevenson <estevenson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607659}
parent 5dea3e1e
...@@ -11,6 +11,7 @@ import android.content.Context; ...@@ -11,6 +11,7 @@ import android.content.Context;
import android.content.pm.InstrumentationInfo; import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.internal.runner.RunnerArgs; import android.support.test.internal.runner.RunnerArgs;
...@@ -183,20 +184,26 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner { ...@@ -183,20 +184,26 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
} }
private TestRequest createListTestRequest(Bundle arguments) { private TestRequest createListTestRequest(Bundle arguments) {
DexFile[] incrementalJars = null; List<DexFile> dexFiles = new ArrayList<>();
try { try {
Class<?> bootstrapClass = Class<?> bootstrapClass =
Class.forName("org.chromium.incrementalinstall.BootstrapApplication"); Class.forName("org.chromium.incrementalinstall.BootstrapApplication");
incrementalJars = dexFiles = Arrays.asList(
(DexFile[]) bootstrapClass.getDeclaredField("sIncrementalDexFiles").get(null); (DexFile[]) bootstrapClass.getDeclaredField("sIncrementalDexFiles").get(null));
} catch (Exception e) { } catch (Exception e) {
// Not an incremental apk. // Not an incremental apk.
if (BuildConfig.IS_MULTIDEX_ENABLED
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
// Test listing fails for test classes that aren't in the main dex
// (crbug.com/903820).
addClassloaderDexFiles(dexFiles, getClass().getClassLoader());
}
} }
RunnerArgs runnerArgs = RunnerArgs runnerArgs =
new RunnerArgs.Builder().fromManifest(this).fromBundle(arguments).build(); new RunnerArgs.Builder().fromManifest(this).fromBundle(arguments).build();
TestRequestBuilder builder; TestRequestBuilder builder;
if (incrementalJars != null) { if (dexFiles != null) {
builder = new IncrementalInstallTestRequestBuilder(this, arguments, incrementalJars); builder = new DexFileTestRequestBuilder(this, arguments, dexFiles);
} else { } else {
builder = new TestRequestBuilder(this, arguments); builder = new TestRequestBuilder(this, arguments);
} }
...@@ -213,21 +220,24 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner { ...@@ -213,21 +220,24 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
} }
/** /**
* Wraps TestRequestBuilder to make it work with incremental install. * Wraps TestRequestBuilder to make it work with incremental install and for multidex <= K.
*
* TestRequestBuilder does not know to look through the incremental install dex files, and has * TestRequestBuilder does not know to look through the incremental install dex files, and has
* no api for telling it to do so. This class checks to see if the list of tests was given * no api for telling it to do so. This class checks to see if the list of tests was given
* by the runner (mHasClassList), and if not overrides the auto-detection logic in build() * by the runner (mHasClassList), and if not overrides the auto-detection logic in build()
* to manually scan all .dex files. * to manually scan all .dex files.
*
* On <= K, classes not in the main dex file are missed, so we manually list them by grabbing
* the loaded DexFiles from the ClassLoader.
*/ */
private static class IncrementalInstallTestRequestBuilder extends TestRequestBuilder { private static class DexFileTestRequestBuilder extends TestRequestBuilder {
final List<String> mExcludedPrefixes = new ArrayList<String>(); final List<String> mExcludedPrefixes = new ArrayList<String>();
final DexFile[] mIncrementalJars; final List<DexFile> mDexFiles;
boolean mHasClassList; boolean mHasClassList;
IncrementalInstallTestRequestBuilder( DexFileTestRequestBuilder(Instrumentation instr, Bundle bundle, List<DexFile> dexFiles) {
Instrumentation instr, Bundle bundle, DexFile[] incrementalJars) {
super(instr, bundle); super(instr, bundle);
mIncrementalJars = incrementalJars; mDexFiles = dexFiles;
try { try {
Field excludedPackagesField = Field excludedPackagesField =
TestRequestBuilder.class.getDeclaredField("DEFAULT_EXCLUDED_PACKAGES"); TestRequestBuilder.class.getDeclaredField("DEFAULT_EXCLUDED_PACKAGES");
...@@ -284,7 +294,7 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner { ...@@ -284,7 +294,7 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
Log.i(TAG, "Scanning incremental classpath."); Log.i(TAG, "Scanning incremental classpath.");
// Mirror TestRequestBuilder.getClassNamesFromClassPath(). // Mirror TestRequestBuilder.getClassNamesFromClassPath().
TestLoader loader = new TestLoader(); TestLoader loader = new TestLoader();
for (DexFile dexFile : mIncrementalJars) { for (DexFile dexFile : mDexFiles) {
Enumeration<String> classNames = dexFile.entries(); Enumeration<String> classNames = dexFile.entries();
while (classNames.hasMoreElements()) { while (classNames.hasMoreElements()) {
String className = classNames.nextElement(); String className = classNames.nextElement();
...@@ -296,4 +306,25 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner { ...@@ -296,4 +306,25 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
} }
} }
} }
private static Object getField(Class<?> clazz, Object instance, String name)
throws ReflectiveOperationException {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(instance);
}
private static void addClassloaderDexFiles(List<DexFile> dexFiles, ClassLoader cl) {
try {
Object pathList = getField(cl.getClass().getSuperclass(), cl, "pathList");
Object[] dexElements =
(Object[]) getField(pathList.getClass(), pathList, "dexElements");
for (Object dexElement : dexElements) {
dexFiles.add((DexFile) getField(dexElement.getClass(), dexElement, "dexFile"));
}
} catch (Exception e) {
// No way to recover and test listing will fail.
throw new RuntimeException(e);
}
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment