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;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.internal.runner.RunnerArgs;
......@@ -183,20 +184,26 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
}
private TestRequest createListTestRequest(Bundle arguments) {
DexFile[] incrementalJars = null;
List<DexFile> dexFiles = new ArrayList<>();
try {
Class<?> bootstrapClass =
Class.forName("org.chromium.incrementalinstall.BootstrapApplication");
incrementalJars =
(DexFile[]) bootstrapClass.getDeclaredField("sIncrementalDexFiles").get(null);
dexFiles = Arrays.asList(
(DexFile[]) bootstrapClass.getDeclaredField("sIncrementalDexFiles").get(null));
} catch (Exception e) {
// 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 =
new RunnerArgs.Builder().fromManifest(this).fromBundle(arguments).build();
TestRequestBuilder builder;
if (incrementalJars != null) {
builder = new IncrementalInstallTestRequestBuilder(this, arguments, incrementalJars);
if (dexFiles != null) {
builder = new DexFileTestRequestBuilder(this, arguments, dexFiles);
} else {
builder = new TestRequestBuilder(this, arguments);
}
......@@ -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
* 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()
* 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 DexFile[] mIncrementalJars;
final List<DexFile> mDexFiles;
boolean mHasClassList;
IncrementalInstallTestRequestBuilder(
Instrumentation instr, Bundle bundle, DexFile[] incrementalJars) {
DexFileTestRequestBuilder(Instrumentation instr, Bundle bundle, List<DexFile> dexFiles) {
super(instr, bundle);
mIncrementalJars = incrementalJars;
mDexFiles = dexFiles;
try {
Field excludedPackagesField =
TestRequestBuilder.class.getDeclaredField("DEFAULT_EXCLUDED_PACKAGES");
......@@ -284,7 +294,7 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
Log.i(TAG, "Scanning incremental classpath.");
// Mirror TestRequestBuilder.getClassNamesFromClassPath().
TestLoader loader = new TestLoader();
for (DexFile dexFile : mIncrementalJars) {
for (DexFile dexFile : mDexFiles) {
Enumeration<String> classNames = dexFile.entries();
while (classNames.hasMoreElements()) {
String className = classNames.nextElement();
......@@ -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