Loading core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm.test.filters; import android.os.Bundle; import com.android.test.filters.SelectTest; /** * JUnit test filter that select Window Manager Service related tests from FrameworksCoreTests. * * <p>Use this filter when running FrameworksCoreTests as * <pre> * adb shell am instrument -w \ * -e filter com.android.server.wm.test.filters.CoreTestsFilter \ * -e selectTest_verbose true \ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner * </pre> */ public final class CoreTestsFilter extends SelectTest { private static final String[] SELECTED_CORE_TESTS = { "android.app.servertransaction.", // all tests under the package. "android.view.DisplayCutoutTest", }; public CoreTestsFilter(Bundle testArgs) { super(addSelectTest(testArgs, SELECTED_CORE_TESTS)); } } services/tests/wmtests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ android_test { ], static_libs: [ "frameworks-base-testutils", "androidx.test.runner", "mockito-target-minus-junit4", "platform-test-annotations", Loading tests/utils/testutils/java/com/android/test/filters/SelectTest.java 0 → 100644 +338 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.filters; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import org.junit.runner.Description; import org.junit.runner.manipulation.Filter; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.StringJoiner; /** * JUnit filter to select tests. * * <p>This filter selects tests specified by package name, class name, and method name. With this * filter, the package and the class options of AndroidJUnitRunner can be superseded. Also the * restriction that prevents using the package and the class options can be mitigated. * * <p><b>Select out tests from Java packages:</b> this option supersedes {@code -e package} option. * <pre> * adb shell am instrument -w \ * -e filter com.android.test.filters.SelectTest \ * -e selectTest package1.,package2. \ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner * </pre> * Note that the ending {@code .} in package name is mandatory. * * <p><b>Select out test classes:</b> this option supersedes {@code -e class} option. * <pre> * adb shell am instrument -w \ * -e filter com.android.test.filters.SelectTest \ * -e selectTest package1.ClassA,package2.ClassB \ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner * </pre> * * <p><b>Select out test methods from Java classes:</b> * <pre> * adb shell am instrument -w \ * -e filter com.android.test.filters.SelectTest \ * -e selectTest package1.ClassA#methodX,package2.ClassB#methodY \ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner * </pre> * * Those options can be used simultaneously. For example * <pre> * adb shell am instrument -w \ * -e filter com.android.test.filters.SelectTest \ * -e selectTest package1.,package2.classA,package3.ClassB#methodZ \ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner * </pre> * will select out all tests in package1, all tests in classA, and ClassB#methodZ test. * * <p>Note that when this option is specified with either {@code -e package} or {@code -e class} * option, filtering behaves as logically conjunction. Other options, such as {@code -e notPackage}, * {@code -e notClass}, {@code -e annotation}, and {@code -e notAnnotation}, should work as expected * with this SelectTest option. * * <p>When specified with {@code -e selectTest_verbose true} option, {@link SelectTest} verbosely * logs to logcat while parsing {@code -e selectTest} option. */ public class SelectTest extends Filter { private static final String TAG = SelectTest.class.getSimpleName(); @VisibleForTesting static final String OPTION_SELECT_TEST = "selectTest"; @VisibleForTesting static final String OPTION_SELECT_TEST_VERBOSE = OPTION_SELECT_TEST + "_verbose"; private static final String ARGUMENT_ITEM_SEPARATOR = ","; private static final String PACKAGE_NAME_SEPARATOR = "."; private static final String METHOD_SEPARATOR = "#"; @Nullable private final PackageSet mPackageSet; /** * Construct {@link SelectTest} filter from instrumentation arguments in {@link Bundle}. * * @param testArgs instrumentation test arguments. */ public SelectTest(@NonNull Bundle testArgs) { mPackageSet = parseSelectTest(testArgs); } @Override public boolean shouldRun(Description description) { if (mPackageSet == null) { // Accept all tests because this filter is disabled. return true; } String testClassName = description.getClassName(); String testMethodName = description.getMethodName(); return mPackageSet.accept(testClassName, testMethodName); } @Override public String describe() { return OPTION_SELECT_TEST + "=" + mPackageSet; } /** * Create {@link #OPTION_SELECT_TEST} argument and add it to {@code testArgs}. * * <p>This method is intended to be used at constructor of extended {@link Filter} class. * * @param testArgs instrumentation test arguments. * @param selectTests array of class name to be selected to run. * @return modified instrumentation test arguments. */ @NonNull protected static Bundle addSelectTest( @NonNull Bundle testArgs, @NonNull String... selectTests) { if (selectTests.length == 0) { return testArgs; } testArgs.putString(OPTION_SELECT_TEST, join(Arrays.asList(selectTests))); return testArgs; } /** * Parse {@code -e selectTest} argument. * @param testArgs instrumentation test arguments. * @return {@link PackageSet} that will filter tests. Returns {@code null} when no * {@code -e selectTest} option is specified, thus this filter gets disabled. */ @Nullable private static PackageSet parseSelectTest(Bundle testArgs) { final String selectTestArgs = testArgs.getString(OPTION_SELECT_TEST); if (selectTestArgs == null) { Log.w(TAG, "Disabled because no " + OPTION_SELECT_TEST + " option specified"); return null; } final boolean verbose = new Boolean(testArgs.getString(OPTION_SELECT_TEST_VERBOSE)); final PackageSet packageSet = new PackageSet(verbose); for (String selectTestArg : selectTestArgs.split(ARGUMENT_ITEM_SEPARATOR)) { packageSet.add(selectTestArg); } return packageSet; } private static String getPackageName(String selectTestArg) { int endPackagePos = selectTestArg.lastIndexOf(PACKAGE_NAME_SEPARATOR); return (endPackagePos < 0) ? "" : selectTestArg.substring(0, endPackagePos); } @Nullable private static String getClassName(String selectTestArg) { if (selectTestArg.endsWith(PACKAGE_NAME_SEPARATOR)) { return null; } int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR); return (methodSepPos < 0) ? selectTestArg : selectTestArg.substring(0, methodSepPos); } @Nullable private static String getMethodName(String selectTestArg) { int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR); return (methodSepPos < 0) ? null : selectTestArg.substring(methodSepPos + 1); } /** Package level filter */ private static class PackageSet { private final boolean mVerbose; /** * Java package name to {@link ClassSet} map. To represent package filtering, a map value * can be {@code null}. */ private final Map<String, ClassSet> mClassSetMap = new LinkedHashMap<>(); PackageSet(boolean verbose) { mVerbose = verbose; } void add(final String selectTestArg) { final String packageName = getPackageName(selectTestArg); final String className = getClassName(selectTestArg); if (className == null) { ClassSet classSet = mClassSetMap.put(packageName, null); // package filtering. if (mVerbose) { logging("Select package " + selectTestArg, classSet != null, "; supersede " + classSet); } return; } ClassSet classSet = mClassSetMap.get(packageName); if (classSet == null) { if (mClassSetMap.containsKey(packageName)) { if (mVerbose) { logging("Select package " + packageName + PACKAGE_NAME_SEPARATOR, true, " ignore " + selectTestArg); } return; } classSet = new ClassSet(mVerbose); mClassSetMap.put(packageName, classSet); } classSet.add(selectTestArg); } boolean accept(String className, @Nullable String methodName) { String packageName = getPackageName(className); if (!mClassSetMap.containsKey(packageName)) { return false; } ClassSet classSet = mClassSetMap.get(packageName); return classSet == null || classSet.accept(className, methodName); } @Override public String toString() { StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR); for (String packageName : mClassSetMap.keySet()) { ClassSet classSet = mClassSetMap.get(packageName); joiner.add(classSet == null ? packageName + PACKAGE_NAME_SEPARATOR : classSet.toString()); } return joiner.toString(); } } /** Class level filter */ private static class ClassSet { private final boolean mVerbose; /** * Java class name to set of method names map. To represent class filtering, a map value * can be {@code null}. */ private final Map<String, Set<String>> mMethodSetMap = new LinkedHashMap<>(); ClassSet(boolean verbose) { mVerbose = verbose; } void add(String selectTestArg) { final String className = getClassName(selectTestArg); final String methodName = getMethodName(selectTestArg); if (methodName == null) { Set<String> methodSet = mMethodSetMap.put(className, null); // class filtering. if (mVerbose) { logging("Select class " + selectTestArg, methodSet != null, "; supersede " + toString(className, methodSet)); } return; } Set<String> methodSet = mMethodSetMap.get(className); if (methodSet == null) { if (mMethodSetMap.containsKey(className)) { if (mVerbose) { logging("Select class " + className, true, "; ignore " + selectTestArg); } return; } methodSet = new LinkedHashSet<>(); mMethodSetMap.put(className, methodSet); } methodSet.add(methodName); if (mVerbose) { logging("Select method " + selectTestArg, false, null); } } boolean accept(String className, @Nullable String methodName) { if (!mMethodSetMap.containsKey(className)) { return false; } Set<String> methodSet = mMethodSetMap.get(className); return methodName == null || methodSet == null || methodSet.contains(methodName); } @Override public String toString() { StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR); for (String className : mMethodSetMap.keySet()) { joiner.add(toString(className, mMethodSetMap.get(className))); } return joiner.toString(); } private static String toString(String className, @Nullable Set<String> methodSet) { if (methodSet == null) { return className; } StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR); for (String methodName : methodSet) { joiner.add(className + METHOD_SEPARATOR + methodName); } return joiner.toString(); } } private static void logging(String infoLog, boolean isWarning, String warningLog) { if (isWarning) { Log.w(TAG, infoLog + warningLog); } else { Log.i(TAG, infoLog); } } private static String join(Collection<String> list) { StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR); for (String text : list) { joiner.add(text); } return joiner.toString(); } } tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java 0 → 100644 +220 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.filters; import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST; import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST_VERBOSE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Bundle; import android.util.ArraySet; import org.junit.Before; import org.junit.Test; import org.junit.runner.Description; import org.junit.runner.manipulation.Filter; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import java.util.StringJoiner; public class SelectTestTests { private static final String PACKAGE_A = "packageA."; private static final String PACKAGE_B = "packageB."; private static final String PACKAGE_C = "packageC."; private static final String CLASS_A1 = PACKAGE_A + "Class1"; private static final String CLASS_A2 = PACKAGE_A + "Class2"; private static final String CLASS_B3 = PACKAGE_B + "Class3"; private static final String CLASS_B4 = PACKAGE_B + "Class4"; private static final String CLASS_C5 = PACKAGE_C + "Class5"; private static final String CLASS_C6 = PACKAGE_C + "Class6"; private static final String METHOD_A1K = CLASS_A1 + "#methodK"; private static final String METHOD_A1L = CLASS_A1 + "#methodL"; private static final String METHOD_A2M = CLASS_A2 + "#methodM"; private static final String METHOD_A2N = CLASS_A2 + "#methodN"; private static final String METHOD_B3P = CLASS_B3 + "#methodP"; private static final String METHOD_B3Q = CLASS_B3 + "#methodQ"; private static final String METHOD_B4R = CLASS_B4 + "#methodR"; private static final String METHOD_B4S = CLASS_B4 + "#methodS"; private static final String METHOD_C5W = CLASS_C5 + "#methodW"; private static final String METHOD_C5X = CLASS_C5 + "#methodX"; private static final String METHOD_C6Y = CLASS_C6 + "#methodY"; private static final String METHOD_C6Z = CLASS_C6 + "#methodZ"; private static final Set<Description> TEST_METHOD_A1K = methodTest(METHOD_A1K); private static final Set<Description> TEST_METHOD_A1L = methodTest(METHOD_A1L); private static final Set<Description> TEST_METHOD_A2M = methodTest(METHOD_A2M); private static final Set<Description> TEST_METHOD_A2N = methodTest(METHOD_A2N); private static final Set<Description> TEST_METHOD_B3P = methodTest(METHOD_B3P); private static final Set<Description> TEST_METHOD_B3Q = methodTest(METHOD_B3Q); private static final Set<Description> TEST_METHOD_B4R = methodTest(METHOD_B4R); private static final Set<Description> TEST_METHOD_B4S = methodTest(METHOD_B4S); private static final Set<Description> TEST_METHOD_C5W = methodTest(METHOD_C5W); private static final Set<Description> TEST_METHOD_C5X = methodTest(METHOD_C5X); private static final Set<Description> TEST_METHOD_C6Y = methodTest(METHOD_C6Y); private static final Set<Description> TEST_METHOD_C6Z = methodTest(METHOD_C6Z); private static final Set<Description> TEST_CLASS_A1 = merge(TEST_METHOD_A1K, TEST_METHOD_A1L); private static final Set<Description> TEST_CLASS_A2 = merge(TEST_METHOD_A2M, TEST_METHOD_A2N); private static final Set<Description> TEST_CLASS_B3 = merge(TEST_METHOD_B3P, TEST_METHOD_B3Q); private static final Set<Description> TEST_CLASS_B4 = merge(TEST_METHOD_B4R, TEST_METHOD_B4S); private static final Set<Description> TEST_CLASS_C5 = merge(TEST_METHOD_C5W, TEST_METHOD_C5X); private static final Set<Description> TEST_CLASS_C6 = merge(TEST_METHOD_C6Y, TEST_METHOD_C6Z); private static final Set<Description> TEST_PACKAGE_A = merge(TEST_CLASS_A1, TEST_CLASS_A2); private static final Set<Description> TEST_PACKAGE_B = merge(TEST_CLASS_B3, TEST_CLASS_B4); private static final Set<Description> TEST_PACKAGE_C = merge(TEST_CLASS_C5, TEST_CLASS_C6); private static final Set<Description> TEST_ALL = merge(TEST_PACKAGE_A, TEST_PACKAGE_B, TEST_PACKAGE_C); private SelectTestBuilder mBuilder; @Before public void setUp() { mBuilder = new SelectTestBuilder(); } private static class SelectTestBuilder { private final Bundle mTestArgs = new Bundle(); Filter build() { mTestArgs.putString(OPTION_SELECT_TEST_VERBOSE, Boolean.TRUE.toString()); return new SelectTest(mTestArgs); } SelectTestBuilder withSelectTest(String... selectTestArgs) { putTestOption(OPTION_SELECT_TEST, selectTestArgs); return this; } private void putTestOption(String option, String... args) { if (args.length > 0) { StringJoiner joiner = new StringJoiner(","); for (String arg : args) { joiner.add(arg); } mTestArgs.putString(option, joiner.toString()); } } } private static Set<Description> methodTest(String testName) { int methodSep = testName.indexOf("#"); String className = testName.substring(0, methodSep); String methodName = testName.substring(methodSep + 1); final Set<Description> tests = new ArraySet<>(); tests.add(Description.createSuiteDescription(className)); tests.add(Description.createTestDescription(className, methodName)); return Collections.unmodifiableSet(tests); } @SafeVarargs private static Set<Description> merge(Set<Description>... testSpecs) { final Set<Description> merged = new LinkedHashSet<>(); for (Set<Description> testSet : testSpecs) { merged.addAll(testSet); } return Collections.unmodifiableSet(merged); } @SafeVarargs private static void acceptTests(Filter filter, Set<Description>... testSpecs) { final Set<Description> accepts = merge(testSpecs); for (Description test : TEST_ALL) { if (accepts.contains(test)) { assertTrue("accept " + test, filter.shouldRun(test)); } else { assertFalse("reject " + test, filter.shouldRun(test)); } } } @Test public void testFilterDisabled() { final Filter filter = mBuilder.build(); acceptTests(filter, TEST_ALL); } @Test public void testSelectPackage() { final Filter filter = mBuilder.withSelectTest(PACKAGE_A, PACKAGE_B).build(); acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B); } @Test public void testSelectClass() { final Filter filter = mBuilder.withSelectTest(CLASS_A1, CLASS_A2, CLASS_B3).build(); acceptTests(filter, TEST_CLASS_A1, TEST_CLASS_A2, TEST_CLASS_B3); } @Test public void testSelectMethod() { final Filter filter = mBuilder .withSelectTest(METHOD_A1K, METHOD_A2M, METHOD_A2N, METHOD_B3P).build(); acceptTests(filter, TEST_METHOD_A1K, TEST_METHOD_A2M, TEST_METHOD_A2N, TEST_METHOD_B3P); } @Test public void testSelectClassAndPackage() { final Filter filter = mBuilder.withSelectTest(CLASS_A1, PACKAGE_B, CLASS_C5).build(); acceptTests(filter, TEST_CLASS_A1, TEST_PACKAGE_B, TEST_CLASS_C5); } @Test public void testSelectMethodAndPackage() { final Filter filter = mBuilder.withSelectTest(METHOD_A1K, PACKAGE_B, METHOD_C5W).build(); acceptTests(filter, TEST_METHOD_A1K, TEST_PACKAGE_B, TEST_METHOD_C5W); } @Test public void testSelectMethodAndClass() { final Filter filter = mBuilder.withSelectTest(METHOD_A1K, CLASS_C5, METHOD_B3P).build(); acceptTests(filter, TEST_METHOD_A1K, TEST_CLASS_C5, TEST_METHOD_B3P); } @Test public void testSelectClassAndSamePackage() { final Filter filter = mBuilder.withSelectTest( CLASS_A1, PACKAGE_A, CLASS_B3, PACKAGE_C, CLASS_C5).build(); acceptTests(filter, TEST_PACKAGE_A, TEST_CLASS_B3, TEST_PACKAGE_C); } @Test public void testSelectMethodAndSameClass() { final Filter filter = mBuilder.withSelectTest( METHOD_A1K, METHOD_A2M, CLASS_A1, CLASS_B3, METHOD_B3P, METHOD_B4R).build(); acceptTests(filter, TEST_CLASS_A1, TEST_METHOD_A2M, TEST_CLASS_B3, TEST_METHOD_B4R); } @Test public void testSelectMethodAndSamePackage() { final Filter filter = mBuilder.withSelectTest( METHOD_A1K, METHOD_A1L, METHOD_A2M, PACKAGE_A, PACKAGE_C, METHOD_C5W, METHOD_C5X, METHOD_C6Y).build(); acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C); } @Test public void testSelectMethodAndClassAndPackage() { final Filter filter = mBuilder.withSelectTest( METHOD_A1K, CLASS_A1, METHOD_A1L, METHOD_A2M, PACKAGE_A, PACKAGE_B, METHOD_B3Q, CLASS_B3, METHOD_B4R, METHOD_B3P).build(); acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B); } } Loading
core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm.test.filters; import android.os.Bundle; import com.android.test.filters.SelectTest; /** * JUnit test filter that select Window Manager Service related tests from FrameworksCoreTests. * * <p>Use this filter when running FrameworksCoreTests as * <pre> * adb shell am instrument -w \ * -e filter com.android.server.wm.test.filters.CoreTestsFilter \ * -e selectTest_verbose true \ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner * </pre> */ public final class CoreTestsFilter extends SelectTest { private static final String[] SELECTED_CORE_TESTS = { "android.app.servertransaction.", // all tests under the package. "android.view.DisplayCutoutTest", }; public CoreTestsFilter(Bundle testArgs) { super(addSelectTest(testArgs, SELECTED_CORE_TESTS)); } }
services/tests/wmtests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ android_test { ], static_libs: [ "frameworks-base-testutils", "androidx.test.runner", "mockito-target-minus-junit4", "platform-test-annotations", Loading
tests/utils/testutils/java/com/android/test/filters/SelectTest.java 0 → 100644 +338 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.filters; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import org.junit.runner.Description; import org.junit.runner.manipulation.Filter; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.StringJoiner; /** * JUnit filter to select tests. * * <p>This filter selects tests specified by package name, class name, and method name. With this * filter, the package and the class options of AndroidJUnitRunner can be superseded. Also the * restriction that prevents using the package and the class options can be mitigated. * * <p><b>Select out tests from Java packages:</b> this option supersedes {@code -e package} option. * <pre> * adb shell am instrument -w \ * -e filter com.android.test.filters.SelectTest \ * -e selectTest package1.,package2. \ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner * </pre> * Note that the ending {@code .} in package name is mandatory. * * <p><b>Select out test classes:</b> this option supersedes {@code -e class} option. * <pre> * adb shell am instrument -w \ * -e filter com.android.test.filters.SelectTest \ * -e selectTest package1.ClassA,package2.ClassB \ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner * </pre> * * <p><b>Select out test methods from Java classes:</b> * <pre> * adb shell am instrument -w \ * -e filter com.android.test.filters.SelectTest \ * -e selectTest package1.ClassA#methodX,package2.ClassB#methodY \ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner * </pre> * * Those options can be used simultaneously. For example * <pre> * adb shell am instrument -w \ * -e filter com.android.test.filters.SelectTest \ * -e selectTest package1.,package2.classA,package3.ClassB#methodZ \ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner * </pre> * will select out all tests in package1, all tests in classA, and ClassB#methodZ test. * * <p>Note that when this option is specified with either {@code -e package} or {@code -e class} * option, filtering behaves as logically conjunction. Other options, such as {@code -e notPackage}, * {@code -e notClass}, {@code -e annotation}, and {@code -e notAnnotation}, should work as expected * with this SelectTest option. * * <p>When specified with {@code -e selectTest_verbose true} option, {@link SelectTest} verbosely * logs to logcat while parsing {@code -e selectTest} option. */ public class SelectTest extends Filter { private static final String TAG = SelectTest.class.getSimpleName(); @VisibleForTesting static final String OPTION_SELECT_TEST = "selectTest"; @VisibleForTesting static final String OPTION_SELECT_TEST_VERBOSE = OPTION_SELECT_TEST + "_verbose"; private static final String ARGUMENT_ITEM_SEPARATOR = ","; private static final String PACKAGE_NAME_SEPARATOR = "."; private static final String METHOD_SEPARATOR = "#"; @Nullable private final PackageSet mPackageSet; /** * Construct {@link SelectTest} filter from instrumentation arguments in {@link Bundle}. * * @param testArgs instrumentation test arguments. */ public SelectTest(@NonNull Bundle testArgs) { mPackageSet = parseSelectTest(testArgs); } @Override public boolean shouldRun(Description description) { if (mPackageSet == null) { // Accept all tests because this filter is disabled. return true; } String testClassName = description.getClassName(); String testMethodName = description.getMethodName(); return mPackageSet.accept(testClassName, testMethodName); } @Override public String describe() { return OPTION_SELECT_TEST + "=" + mPackageSet; } /** * Create {@link #OPTION_SELECT_TEST} argument and add it to {@code testArgs}. * * <p>This method is intended to be used at constructor of extended {@link Filter} class. * * @param testArgs instrumentation test arguments. * @param selectTests array of class name to be selected to run. * @return modified instrumentation test arguments. */ @NonNull protected static Bundle addSelectTest( @NonNull Bundle testArgs, @NonNull String... selectTests) { if (selectTests.length == 0) { return testArgs; } testArgs.putString(OPTION_SELECT_TEST, join(Arrays.asList(selectTests))); return testArgs; } /** * Parse {@code -e selectTest} argument. * @param testArgs instrumentation test arguments. * @return {@link PackageSet} that will filter tests. Returns {@code null} when no * {@code -e selectTest} option is specified, thus this filter gets disabled. */ @Nullable private static PackageSet parseSelectTest(Bundle testArgs) { final String selectTestArgs = testArgs.getString(OPTION_SELECT_TEST); if (selectTestArgs == null) { Log.w(TAG, "Disabled because no " + OPTION_SELECT_TEST + " option specified"); return null; } final boolean verbose = new Boolean(testArgs.getString(OPTION_SELECT_TEST_VERBOSE)); final PackageSet packageSet = new PackageSet(verbose); for (String selectTestArg : selectTestArgs.split(ARGUMENT_ITEM_SEPARATOR)) { packageSet.add(selectTestArg); } return packageSet; } private static String getPackageName(String selectTestArg) { int endPackagePos = selectTestArg.lastIndexOf(PACKAGE_NAME_SEPARATOR); return (endPackagePos < 0) ? "" : selectTestArg.substring(0, endPackagePos); } @Nullable private static String getClassName(String selectTestArg) { if (selectTestArg.endsWith(PACKAGE_NAME_SEPARATOR)) { return null; } int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR); return (methodSepPos < 0) ? selectTestArg : selectTestArg.substring(0, methodSepPos); } @Nullable private static String getMethodName(String selectTestArg) { int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR); return (methodSepPos < 0) ? null : selectTestArg.substring(methodSepPos + 1); } /** Package level filter */ private static class PackageSet { private final boolean mVerbose; /** * Java package name to {@link ClassSet} map. To represent package filtering, a map value * can be {@code null}. */ private final Map<String, ClassSet> mClassSetMap = new LinkedHashMap<>(); PackageSet(boolean verbose) { mVerbose = verbose; } void add(final String selectTestArg) { final String packageName = getPackageName(selectTestArg); final String className = getClassName(selectTestArg); if (className == null) { ClassSet classSet = mClassSetMap.put(packageName, null); // package filtering. if (mVerbose) { logging("Select package " + selectTestArg, classSet != null, "; supersede " + classSet); } return; } ClassSet classSet = mClassSetMap.get(packageName); if (classSet == null) { if (mClassSetMap.containsKey(packageName)) { if (mVerbose) { logging("Select package " + packageName + PACKAGE_NAME_SEPARATOR, true, " ignore " + selectTestArg); } return; } classSet = new ClassSet(mVerbose); mClassSetMap.put(packageName, classSet); } classSet.add(selectTestArg); } boolean accept(String className, @Nullable String methodName) { String packageName = getPackageName(className); if (!mClassSetMap.containsKey(packageName)) { return false; } ClassSet classSet = mClassSetMap.get(packageName); return classSet == null || classSet.accept(className, methodName); } @Override public String toString() { StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR); for (String packageName : mClassSetMap.keySet()) { ClassSet classSet = mClassSetMap.get(packageName); joiner.add(classSet == null ? packageName + PACKAGE_NAME_SEPARATOR : classSet.toString()); } return joiner.toString(); } } /** Class level filter */ private static class ClassSet { private final boolean mVerbose; /** * Java class name to set of method names map. To represent class filtering, a map value * can be {@code null}. */ private final Map<String, Set<String>> mMethodSetMap = new LinkedHashMap<>(); ClassSet(boolean verbose) { mVerbose = verbose; } void add(String selectTestArg) { final String className = getClassName(selectTestArg); final String methodName = getMethodName(selectTestArg); if (methodName == null) { Set<String> methodSet = mMethodSetMap.put(className, null); // class filtering. if (mVerbose) { logging("Select class " + selectTestArg, methodSet != null, "; supersede " + toString(className, methodSet)); } return; } Set<String> methodSet = mMethodSetMap.get(className); if (methodSet == null) { if (mMethodSetMap.containsKey(className)) { if (mVerbose) { logging("Select class " + className, true, "; ignore " + selectTestArg); } return; } methodSet = new LinkedHashSet<>(); mMethodSetMap.put(className, methodSet); } methodSet.add(methodName); if (mVerbose) { logging("Select method " + selectTestArg, false, null); } } boolean accept(String className, @Nullable String methodName) { if (!mMethodSetMap.containsKey(className)) { return false; } Set<String> methodSet = mMethodSetMap.get(className); return methodName == null || methodSet == null || methodSet.contains(methodName); } @Override public String toString() { StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR); for (String className : mMethodSetMap.keySet()) { joiner.add(toString(className, mMethodSetMap.get(className))); } return joiner.toString(); } private static String toString(String className, @Nullable Set<String> methodSet) { if (methodSet == null) { return className; } StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR); for (String methodName : methodSet) { joiner.add(className + METHOD_SEPARATOR + methodName); } return joiner.toString(); } } private static void logging(String infoLog, boolean isWarning, String warningLog) { if (isWarning) { Log.w(TAG, infoLog + warningLog); } else { Log.i(TAG, infoLog); } } private static String join(Collection<String> list) { StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR); for (String text : list) { joiner.add(text); } return joiner.toString(); } }
tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java 0 → 100644 +220 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.filters; import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST; import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST_VERBOSE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Bundle; import android.util.ArraySet; import org.junit.Before; import org.junit.Test; import org.junit.runner.Description; import org.junit.runner.manipulation.Filter; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import java.util.StringJoiner; public class SelectTestTests { private static final String PACKAGE_A = "packageA."; private static final String PACKAGE_B = "packageB."; private static final String PACKAGE_C = "packageC."; private static final String CLASS_A1 = PACKAGE_A + "Class1"; private static final String CLASS_A2 = PACKAGE_A + "Class2"; private static final String CLASS_B3 = PACKAGE_B + "Class3"; private static final String CLASS_B4 = PACKAGE_B + "Class4"; private static final String CLASS_C5 = PACKAGE_C + "Class5"; private static final String CLASS_C6 = PACKAGE_C + "Class6"; private static final String METHOD_A1K = CLASS_A1 + "#methodK"; private static final String METHOD_A1L = CLASS_A1 + "#methodL"; private static final String METHOD_A2M = CLASS_A2 + "#methodM"; private static final String METHOD_A2N = CLASS_A2 + "#methodN"; private static final String METHOD_B3P = CLASS_B3 + "#methodP"; private static final String METHOD_B3Q = CLASS_B3 + "#methodQ"; private static final String METHOD_B4R = CLASS_B4 + "#methodR"; private static final String METHOD_B4S = CLASS_B4 + "#methodS"; private static final String METHOD_C5W = CLASS_C5 + "#methodW"; private static final String METHOD_C5X = CLASS_C5 + "#methodX"; private static final String METHOD_C6Y = CLASS_C6 + "#methodY"; private static final String METHOD_C6Z = CLASS_C6 + "#methodZ"; private static final Set<Description> TEST_METHOD_A1K = methodTest(METHOD_A1K); private static final Set<Description> TEST_METHOD_A1L = methodTest(METHOD_A1L); private static final Set<Description> TEST_METHOD_A2M = methodTest(METHOD_A2M); private static final Set<Description> TEST_METHOD_A2N = methodTest(METHOD_A2N); private static final Set<Description> TEST_METHOD_B3P = methodTest(METHOD_B3P); private static final Set<Description> TEST_METHOD_B3Q = methodTest(METHOD_B3Q); private static final Set<Description> TEST_METHOD_B4R = methodTest(METHOD_B4R); private static final Set<Description> TEST_METHOD_B4S = methodTest(METHOD_B4S); private static final Set<Description> TEST_METHOD_C5W = methodTest(METHOD_C5W); private static final Set<Description> TEST_METHOD_C5X = methodTest(METHOD_C5X); private static final Set<Description> TEST_METHOD_C6Y = methodTest(METHOD_C6Y); private static final Set<Description> TEST_METHOD_C6Z = methodTest(METHOD_C6Z); private static final Set<Description> TEST_CLASS_A1 = merge(TEST_METHOD_A1K, TEST_METHOD_A1L); private static final Set<Description> TEST_CLASS_A2 = merge(TEST_METHOD_A2M, TEST_METHOD_A2N); private static final Set<Description> TEST_CLASS_B3 = merge(TEST_METHOD_B3P, TEST_METHOD_B3Q); private static final Set<Description> TEST_CLASS_B4 = merge(TEST_METHOD_B4R, TEST_METHOD_B4S); private static final Set<Description> TEST_CLASS_C5 = merge(TEST_METHOD_C5W, TEST_METHOD_C5X); private static final Set<Description> TEST_CLASS_C6 = merge(TEST_METHOD_C6Y, TEST_METHOD_C6Z); private static final Set<Description> TEST_PACKAGE_A = merge(TEST_CLASS_A1, TEST_CLASS_A2); private static final Set<Description> TEST_PACKAGE_B = merge(TEST_CLASS_B3, TEST_CLASS_B4); private static final Set<Description> TEST_PACKAGE_C = merge(TEST_CLASS_C5, TEST_CLASS_C6); private static final Set<Description> TEST_ALL = merge(TEST_PACKAGE_A, TEST_PACKAGE_B, TEST_PACKAGE_C); private SelectTestBuilder mBuilder; @Before public void setUp() { mBuilder = new SelectTestBuilder(); } private static class SelectTestBuilder { private final Bundle mTestArgs = new Bundle(); Filter build() { mTestArgs.putString(OPTION_SELECT_TEST_VERBOSE, Boolean.TRUE.toString()); return new SelectTest(mTestArgs); } SelectTestBuilder withSelectTest(String... selectTestArgs) { putTestOption(OPTION_SELECT_TEST, selectTestArgs); return this; } private void putTestOption(String option, String... args) { if (args.length > 0) { StringJoiner joiner = new StringJoiner(","); for (String arg : args) { joiner.add(arg); } mTestArgs.putString(option, joiner.toString()); } } } private static Set<Description> methodTest(String testName) { int methodSep = testName.indexOf("#"); String className = testName.substring(0, methodSep); String methodName = testName.substring(methodSep + 1); final Set<Description> tests = new ArraySet<>(); tests.add(Description.createSuiteDescription(className)); tests.add(Description.createTestDescription(className, methodName)); return Collections.unmodifiableSet(tests); } @SafeVarargs private static Set<Description> merge(Set<Description>... testSpecs) { final Set<Description> merged = new LinkedHashSet<>(); for (Set<Description> testSet : testSpecs) { merged.addAll(testSet); } return Collections.unmodifiableSet(merged); } @SafeVarargs private static void acceptTests(Filter filter, Set<Description>... testSpecs) { final Set<Description> accepts = merge(testSpecs); for (Description test : TEST_ALL) { if (accepts.contains(test)) { assertTrue("accept " + test, filter.shouldRun(test)); } else { assertFalse("reject " + test, filter.shouldRun(test)); } } } @Test public void testFilterDisabled() { final Filter filter = mBuilder.build(); acceptTests(filter, TEST_ALL); } @Test public void testSelectPackage() { final Filter filter = mBuilder.withSelectTest(PACKAGE_A, PACKAGE_B).build(); acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B); } @Test public void testSelectClass() { final Filter filter = mBuilder.withSelectTest(CLASS_A1, CLASS_A2, CLASS_B3).build(); acceptTests(filter, TEST_CLASS_A1, TEST_CLASS_A2, TEST_CLASS_B3); } @Test public void testSelectMethod() { final Filter filter = mBuilder .withSelectTest(METHOD_A1K, METHOD_A2M, METHOD_A2N, METHOD_B3P).build(); acceptTests(filter, TEST_METHOD_A1K, TEST_METHOD_A2M, TEST_METHOD_A2N, TEST_METHOD_B3P); } @Test public void testSelectClassAndPackage() { final Filter filter = mBuilder.withSelectTest(CLASS_A1, PACKAGE_B, CLASS_C5).build(); acceptTests(filter, TEST_CLASS_A1, TEST_PACKAGE_B, TEST_CLASS_C5); } @Test public void testSelectMethodAndPackage() { final Filter filter = mBuilder.withSelectTest(METHOD_A1K, PACKAGE_B, METHOD_C5W).build(); acceptTests(filter, TEST_METHOD_A1K, TEST_PACKAGE_B, TEST_METHOD_C5W); } @Test public void testSelectMethodAndClass() { final Filter filter = mBuilder.withSelectTest(METHOD_A1K, CLASS_C5, METHOD_B3P).build(); acceptTests(filter, TEST_METHOD_A1K, TEST_CLASS_C5, TEST_METHOD_B3P); } @Test public void testSelectClassAndSamePackage() { final Filter filter = mBuilder.withSelectTest( CLASS_A1, PACKAGE_A, CLASS_B3, PACKAGE_C, CLASS_C5).build(); acceptTests(filter, TEST_PACKAGE_A, TEST_CLASS_B3, TEST_PACKAGE_C); } @Test public void testSelectMethodAndSameClass() { final Filter filter = mBuilder.withSelectTest( METHOD_A1K, METHOD_A2M, CLASS_A1, CLASS_B3, METHOD_B3P, METHOD_B4R).build(); acceptTests(filter, TEST_CLASS_A1, TEST_METHOD_A2M, TEST_CLASS_B3, TEST_METHOD_B4R); } @Test public void testSelectMethodAndSamePackage() { final Filter filter = mBuilder.withSelectTest( METHOD_A1K, METHOD_A1L, METHOD_A2M, PACKAGE_A, PACKAGE_C, METHOD_C5W, METHOD_C5X, METHOD_C6Y).build(); acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C); } @Test public void testSelectMethodAndClassAndPackage() { final Filter filter = mBuilder.withSelectTest( METHOD_A1K, CLASS_A1, METHOD_A1L, METHOD_A2M, PACKAGE_A, PACKAGE_B, METHOD_B3Q, CLASS_B3, METHOD_B4R, METHOD_B3P).build(); acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B); } }