| Index: base/test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java
|
| diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fcda91039be63d15930700cc63a94204b9dda9b0
|
| --- /dev/null
|
| +++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java
|
| @@ -0,0 +1,162 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.base.test;
|
| +
|
| +import android.content.Context;
|
| +import android.content.ContextWrapper;
|
| +import android.content.SharedPreferences;
|
| +import android.content.pm.ApplicationInfo;
|
| +import android.content.pm.PackageManager;
|
| +
|
| +import org.chromium.android.support.PackageManagerWrapper;
|
| +import org.chromium.base.Log;
|
| +import org.chromium.base.annotations.MainDex;
|
| +
|
| +import java.io.File;
|
| +import java.io.IOException;
|
| +import java.io.Serializable;
|
| +import java.lang.reflect.Field;
|
| +import java.util.Arrays;
|
| +import java.util.Comparator;
|
| +
|
| +/**
|
| + * Functionality common to the JUnit3 and JUnit4 runners.
|
| + */
|
| +@MainDex
|
| +class BaseChromiumRunnerCommon {
|
| + private static final String TAG = "base_test";
|
| +
|
| + /**
|
| + * A ContextWrapper that allows multidex test APKs to extract secondary dexes into
|
| + * the APK under test's data directory.
|
| + */
|
| + @MainDex
|
| + static class MultiDexContextWrapper extends ContextWrapper {
|
| + private Context mAppContext;
|
| +
|
| + MultiDexContextWrapper(Context instrContext, Context appContext) {
|
| + super(instrContext);
|
| + mAppContext = appContext;
|
| + }
|
| +
|
| + @Override
|
| + public File getFilesDir() {
|
| + return mAppContext.getFilesDir();
|
| + }
|
| +
|
| + @Override
|
| + public SharedPreferences getSharedPreferences(String name, int mode) {
|
| + return mAppContext.getSharedPreferences(name, mode);
|
| + }
|
| +
|
| + @Override
|
| + public PackageManager getPackageManager() {
|
| + return new PackageManagerWrapper(super.getPackageManager()) {
|
| + @Override
|
| + public ApplicationInfo getApplicationInfo(String packageName, int flags) {
|
| + try {
|
| + ApplicationInfo ai = super.getApplicationInfo(packageName, flags);
|
| + if (packageName.equals(getPackageName())) {
|
| + ApplicationInfo appAi =
|
| + super.getApplicationInfo(mAppContext.getPackageName(), flags);
|
| + File dataDir = new File(appAi.dataDir, "test-multidex");
|
| + if (!dataDir.exists() && !dataDir.mkdirs()) {
|
| + throw new IOException(String.format(
|
| + "Unable to create test multidex directory \"%s\"",
|
| + dataDir.getPath()));
|
| + }
|
| + ai.dataDir = dataDir.getPath();
|
| + }
|
| + return ai;
|
| + } catch (Exception e) {
|
| + Log.e(TAG, "Failed to get application info for %s", packageName, e);
|
| + }
|
| + return null;
|
| + }
|
| + };
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Ensure all test dex entries precede app dex entries.
|
| + *
|
| + * @param cl ClassLoader to modify. Assumed to be a derivative of
|
| + * {@link dalvik.system.BaseDexClassLoader}. If this isn't
|
| + * the case, reordering will fail.
|
| + */
|
| + static void reorderDexPathElements(ClassLoader cl, Context context, Context targetContext) {
|
| + try {
|
| + Log.i(TAG,
|
| + "Reordering dex files. If you're building a multidex test APK and see a "
|
| + + "class resolving to an unexpected implementation, this may be why.");
|
| + Field pathListField = findField(cl, "pathList");
|
| + Object dexPathList = pathListField.get(cl);
|
| + Field dexElementsField = findField(dexPathList, "dexElements");
|
| + Object[] dexElementsList = (Object[]) dexElementsField.get(dexPathList);
|
| + Arrays.sort(dexElementsList,
|
| + new DexListReorderingComparator(
|
| + context.getPackageName(), targetContext.getPackageName()));
|
| + dexElementsField.set(dexPathList, dexElementsList);
|
| + } catch (Exception e) {
|
| + Log.e(TAG, "Failed to reorder dex elements for testing.", e);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Comparator for sorting dex list entries.
|
| + *
|
| + * Using this to sort a list of dex list entries will result in the following order:
|
| + * - Strings that contain neither the test package nor the app package in lexicographical
|
| + * order.
|
| + * - Strings that contain the test package in lexicographical order.
|
| + * - Strings that contain the app package but not the test package in lexicographical order.
|
| + */
|
| + private static class DexListReorderingComparator implements Comparator<Object>, Serializable {
|
| + private String mTestPackage;
|
| + private String mAppPackage;
|
| +
|
| + public DexListReorderingComparator(String testPackage, String appPackage) {
|
| + mTestPackage = testPackage;
|
| + mAppPackage = appPackage;
|
| + }
|
| +
|
| + @Override
|
| + public int compare(Object o1, Object o2) {
|
| + String s1 = o1.toString();
|
| + String s2 = o2.toString();
|
| + if (s1.contains(mTestPackage)) {
|
| + if (!s2.contains(mTestPackage)) {
|
| + if (s2.contains(mAppPackage)) {
|
| + return -1;
|
| + } else {
|
| + return 1;
|
| + }
|
| + }
|
| + } else if (s1.contains(mAppPackage)) {
|
| + if (s2.contains(mTestPackage)) {
|
| + return 1;
|
| + } else if (!s2.contains(mAppPackage)) {
|
| + return 1;
|
| + }
|
| + } else if (s2.contains(mTestPackage) || s2.contains(mAppPackage)) {
|
| + return -1;
|
| + }
|
| + return s1.compareTo(s2);
|
| + }
|
| + }
|
| +
|
| + private static Field findField(Object instance, String name) throws NoSuchFieldException {
|
| + for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
|
| + try {
|
| + Field f = clazz.getDeclaredField(name);
|
| + f.setAccessible(true);
|
| + return f;
|
| + } catch (NoSuchFieldException e) {
|
| + }
|
| + }
|
| + throw new NoSuchFieldException(
|
| + "Unable to find field " + name + " in " + instance.getClass());
|
| + }
|
| +}
|
|
|