Loading ravenwood/Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -102,7 +102,8 @@ java_library { sdk_version: "core_current", srcs: ["common-src/**/*.java"], static_libs: [ "framework-annotations-lib", // should it be "libs" instead? "junit", "framework-annotations-lib", "modules-utils-ravenwood", ], visibility: ["//visibility:private"], Loading ravenwood/common-src/com/android/ravenwood/common/RavenwoodInternalUtils.java +64 −14 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import android.annotation.Nullable; import com.android.modules.utils.ravenwood.RavenwoodHelper; import org.junit.runner.Description; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; Loading @@ -43,7 +45,7 @@ public class RavenwoodInternalUtils { /** * If set to "1", we enable the verbose logging. * * <p> * (See also InitLogging() in http://ac/system/libbase/logging.cpp) */ public static final boolean RAVENWOOD_VERBOSE_LOGGING = Loading @@ -69,21 +71,24 @@ public class RavenwoodInternalUtils { return sEnableExtraRuntimeCheck; } /** Simple logging method. */ /** * Simple logging method. */ public static void log(String tag, String message) { // Avoid using Android's Log class, which could be broken for various reasons. // (e.g. the JNI file doesn't exist for whatever reason) System.out.print(tag + ": " + message + "\n"); } /** Simple logging method. */ /** * Simple logging method. */ private void log(String tag, String format, Object... args) { log(tag, String.format(format, args)); } /** * Internal implementation of * {@link android.platform.test.ravenwood.RavenwoodRule#loadJniLibrary(String)} * Internal implementation of {@link android.platform.test.ravenwood.RavenwoodRule#loadJniLibrary(String)} */ public static void loadJniLibrary(String libname) { if (isOnRavenwood()) { Loading Loading @@ -171,14 +176,16 @@ public class RavenwoodInternalUtils { /** * @return the full directory path that contains the "ravenwood-runtime" files. * * <p> * This method throws if called on the device side. */ public static String getRavenwoodRuntimePath() { return RavenwoodHelper.getRavenwoodRuntimePath(); } /** Close an {@link AutoCloseable}. */ /** * Close an {@link AutoCloseable}. */ public static void closeQuietly(AutoCloseable c) { if (c != null) { try { Loading @@ -189,7 +196,9 @@ public class RavenwoodInternalUtils { } } /** Close a {@link FileDescriptor}. */ /** * Close a {@link FileDescriptor}. */ public static void closeQuietly(FileDescriptor fd) { var is = new FileInputStream(fd); closeQuietly(is); Loading Loading @@ -222,7 +231,7 @@ public class RavenwoodInternalUtils { /** * Run a supplier and swallow the exception, if any. * * <p> * It's a dangerous function. Only use it in an exception handler where we don't want to crash. */ @Nullable Loading @@ -237,7 +246,7 @@ public class RavenwoodInternalUtils { /** * Run a runnable and swallow the exception, if any. * * <p> * It's a dangerous function. Only use it in an exception handler where we don't want to crash. */ public static void runIgnoringException(@NonNull Runnable r) { Loading @@ -255,7 +264,9 @@ public class RavenwoodInternalUtils { return stringWriter.toString(); } /** Same as {@link Integer#parseInt(String)} but accepts null and returns null. */ /** * Same as {@link Integer#parseInt(String)} but accepts null and returns null. */ @Nullable public static Integer parseNullableInt(@Nullable String value) { if (value == null) { Loading @@ -278,7 +289,7 @@ public class RavenwoodInternalUtils { /** * Convert a wildcard string using "*" and "**" to match Java classnames. * * <p> * The input string can only contain alnum, "$", "." and "*". */ public static Pattern parseClassNameWildcard(String pattern) { Loading @@ -299,4 +310,43 @@ public class RavenwoodInternalUtils { temp = temp.replace("@@", ".*"); return Pattern.compile(temp); } /** * @return true if it's a junit4 test method. */ public static Boolean isTestMethod(@NonNull Class<?> clazz, @NonNull Method method) { return method.getAnnotation(org.junit.Test.class) != null; } private static final Pattern sParamsPattern = Pattern.compile("\\[.*$"); /** * Take a {@link Description#getMethodName()} result and extract the "real" method name, * i.e. without the parameters. */ public static String getMethodNameFromMethodDescription(@NonNull String methodDescriptionName) { return sParamsPattern.matcher(methodDescriptionName).replaceFirst(""); } /** * @return a canonical name of a test method. {@code clazz} may be a subclass of * {@link Method#getDeclaringClass()}. */ public static String toCanonicalTestName(@NonNull Class<?> clazz, @NonNull Method method) { return clazz.getName() + "#" + method.getName(); } /** * @return a canonical name of a test method. */ public static String toCanonicalTestName(@NonNull Description description) { if (!description.isTest()) { // It's a test class. return description.getClassName(); } else { // It's a test method return description.getClassName() + "#" + getMethodNameFromMethodDescription( description.getMethodName()); } } } No newline at end of file ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java +96 −12 Original line number Diff line number Diff line Loading @@ -15,7 +15,9 @@ */ package android.platform.test.ravenwood; import static com.android.ravenwood.common.RavenwoodInternalUtils.isTestMethod; import static com.android.ravenwood.common.RavenwoodInternalUtils.parseClassNameWildcard; import static com.android.ravenwood.common.RavenwoodInternalUtils.toCanonicalTestName; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -54,6 +56,11 @@ import java.util.regex.Pattern; * * This feature is only used locally. On CI server, we never use it. * * - You can override the above logic and run exact test classes / test methods by * setting a case-insensitive regex to $RAVENWOOD_FORCE_FILTER_REGEX. If it's set, * only the tests that are mathing it would be executed. For example, * running with RAVENWOOD_FORCE_FILTER_REGEX='(MyTestClass1|MyTestClass2#testFoo)' would * execute all tests in MyTestClass1, and only testFoo() in MyTestClass2. */ public abstract class RavenwoodEnablementChecker { private static final String TAG = RavenwoodInternalUtils.TAG; Loading Loading @@ -161,20 +168,26 @@ public abstract class RavenwoodEnablementChecker { */ @VisibleForTesting public static void setDefaultInstance() { sInstance = new RavenwoodEnablementCheckerImpl(getRunMode(), getPolicyFiles()); sInstance = new RavenwoodEnablementCheckerImpl(getRunMode(), getPolicyFiles(), RavenwoodEnvironment.getInstance().getEnvVar("RAVENWOOD_FORCE_FILTER_REGEX", null)); } /** * Force set a checker for testing. */ @VisibleForTesting public static void overrideInstance(@NonNull RunMode runMode, @Nullable String policyText) { public static void overrideInstance( @NonNull RunMode runMode, @Nullable String policyText, @Nullable String overridingPattern ) { try { var parser = new EnablementTextPolicyParser(); if (policyText != null && !policyText.isEmpty()) { parser.parse("[in-memory]", policyText); } sInstance = new RavenwoodEnablementCheckerImpl(runMode, parser.getResult()); sInstance = new RavenwoodEnablementCheckerImpl( runMode, parser.getResult(), overridingPattern); } catch (IOException e) { SneakyThrow.sneakyThrow(e); // IOException shouldn't happen, but just in case } Loading @@ -193,24 +206,39 @@ public abstract class RavenwoodEnablementChecker { /** * Actual logic. This combines the annotation policy with the text policy. */ @VisibleForTesting public static class RavenwoodEnablementCheckerImpl extends RavenwoodEnablementChecker { static class RavenwoodEnablementCheckerImpl extends RavenwoodEnablementChecker { private final RunMode mRunMode; private final PolicyChecker mChecker; public RavenwoodEnablementCheckerImpl(RunMode runMode, String[] policyFiles) { this(runMode, EnablementTextPolicyParser.parsePolicyFiles(policyFiles)); RavenwoodEnablementCheckerImpl( @NonNull RunMode runMode, @NonNull String[] policyFiles, @Nullable String overridingPattern) { this(runMode, EnablementTextPolicyParser.parsePolicyFiles(policyFiles), overridingPattern); } RavenwoodEnablementCheckerImpl(RunMode runMode, PolicyChecker subChecker) { RavenwoodEnablementCheckerImpl( @NonNull RunMode runMode, @NonNull PolicyChecker subChecker, @Nullable String overridingPattern) { this.mRunMode = runMode; var chain = new PolicyCheckerChain(); if (overridingPattern == null || overridingPattern.isEmpty()) { // Annotations always win. chain.add(new AnnotationPolicyChecker()); // Text policy changes the default behavior. chain.add(subChecker); } else { // Use a regex-based filter, and only run the exact matching tests. chain.add(new RegexRunFilter( Pattern.compile(overridingPattern, Pattern.CASE_INSENSITIVE), RunPolicy.Enabled, RunPolicy.NeverRun )); } mChecker = chain; } Loading Loading @@ -592,3 +620,59 @@ class EnablementTextPolicyParser { return true; } } /** * {@link PolicyChecker} based on a {@link Pattern}. */ class RegexRunFilter implements PolicyChecker { private static final String TAG = "RegexRunFilter"; private final Pattern mRegex; private final RunPolicy mMatchingPolicy; private final RunPolicy mNonMatchingPolicy; /** * Constructor. * * @param regex regex to match. We apply it on both classes and methods. * @param matchingPolicy policy for matching items * @param nonMatchingPolicy policy for non-matching items */ public RegexRunFilter( @NonNull Pattern regex, @NonNull RunPolicy matchingPolicy, @NonNull RunPolicy nonMatchingPolicy) { mRegex = regex; mMatchingPolicy = matchingPolicy; mNonMatchingPolicy = nonMatchingPolicy; } private boolean classMatches(Class<?> testClass) { return mRegex.matcher(testClass.getName()).find(); } @Override public RunPolicy getClassPolicy(Class<?> testClass) { if (classMatches(testClass)) { return mMatchingPolicy; } // If the class has any test matching test method, still return "enabled". // (but unmatched methods would be "unspecified".) for (var m : testClass.getMethods()) { if (!isTestMethod(testClass, m)) { continue; } if (mRegex.matcher(toCanonicalTestName(testClass, m)).find()) { return mMatchingPolicy; } } return mNonMatchingPolicy; } @Override public RunPolicy getMethodPolicy(Description description) { if (mRegex.matcher(toCanonicalTestName(description)).find()) { return mMatchingPolicy; } return classMatches(description.getTestClass()) ? mMatchingPolicy : mNonMatchingPolicy; } } ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodEnablementTest.java +112 −2 Original line number Diff line number Diff line Loading @@ -308,8 +308,6 @@ public class RavenwoodEnablementTest extends RavenwoodRunnerTestBase { } } //-- @RunWith(BlockJUnit4ClassRunner.class) // CHECKSTYLE:OFF Generated code @Expected(value = """ Loading Loading @@ -504,4 +502,116 @@ public class RavenwoodEnablementTest extends RavenwoodRunnerTestBase { public void test3() { } } @RunWith(BlockJUnit4ClassRunner.class) // CHECKSTYLE:OFF Generated code @Expected(value = """ testRunStarted: classes testSuiteStarted: classes testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel testStarted: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testAssumptionFailure: got: <false>, expected: is <true> testFinished: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testStarted: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testAssumptionFailure: got: <false>, expected: is <true> testFinished: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testStarted: test3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testFinished: test3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel testSuiteFinished: classes testRunFinished: 3,0,2,0 """,runMode = RunMode.AlsoDisabledTests, enablementPolicy = """ !module RavenwoodCoreTest ** false # disable all classes """, overridingRegex="testwith.*#test3" ) // CHECKSTYLE:ON public static class TestWithPolicyWithForceRegex_methodLevel { public TestWithPolicyWithForceRegex_methodLevel() { } @Test public void test1() { } @Test public void test2() { } @Test @DisabledOnRavenwood public void test3() { } } @RunWith(BlockJUnit4ClassRunner.class) // CHECKSTYLE:OFF Generated code @Expected(value = """ testRunStarted: classes testSuiteStarted: classes testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled testStarted: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testFinished: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testStarted: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testFinished: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testStarted: test3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testFinished: test3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled testSuiteFinished: classes testRunFinished: 3,0,0,0 """,runMode = RunMode.AlsoDisabledTests, enablementPolicy = """ !module RavenwoodCoreTest ** false # disable all classes """, overridingRegex="TestWithPolicy" // Should run all the tests ) // CHECKSTYLE:ON public static class TestWithPolicyWithForceRegex_classLevelEnabled { public TestWithPolicyWithForceRegex_classLevelEnabled() { } @Test public void test1() { } @Test public void test2() { } @Test @DisabledOnRavenwood public void test3() { } } @RunWith(BlockJUnit4ClassRunner.class) // CHECKSTYLE:OFF Generated code @Expected(value = """ testRunStarted: classes testSuiteStarted: classes testSuiteStarted: <init>(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelDisabled) testIgnored: <init>(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelDisabled) testSuiteFinished: <init>(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelDisabled) testSuiteFinished: classes testRunFinished: 0,0,0,1 """,runMode = RunMode.AlsoDisabledTests, enablementPolicy = """ """, overridingRegex="NotMatchingAnyTests" // no tests should be executed ) // CHECKSTYLE:ON public static class TestWithPolicyWithForceRegex_classLevelDisabled { public TestWithPolicyWithForceRegex_classLevelDisabled() { } @Test public void test1() { } @Test public void test2() { } @Test @DisabledOnRavenwood public void test3() { } } } ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java +2 −1 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ public abstract class RavenwoodRunnerTestBase { String value(); RunMode runMode() default RunMode.Normal; String enablementPolicy() default ""; String overridingRegex() default ""; } private static final AtomicReference<Throwable> sError = new AtomicReference<>(); Loading Loading @@ -153,7 +154,7 @@ public abstract class RavenwoodRunnerTestBase { // Oevrride enablement policy RavenwoodEnablementChecker.overrideInstance( expected.runMode(), expected.enablementPolicy()); expected.runMode(), expected.enablementPolicy(), expected.overridingRegex()); sError.set(null); Loading Loading
ravenwood/Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -102,7 +102,8 @@ java_library { sdk_version: "core_current", srcs: ["common-src/**/*.java"], static_libs: [ "framework-annotations-lib", // should it be "libs" instead? "junit", "framework-annotations-lib", "modules-utils-ravenwood", ], visibility: ["//visibility:private"], Loading
ravenwood/common-src/com/android/ravenwood/common/RavenwoodInternalUtils.java +64 −14 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import android.annotation.Nullable; import com.android.modules.utils.ravenwood.RavenwoodHelper; import org.junit.runner.Description; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; Loading @@ -43,7 +45,7 @@ public class RavenwoodInternalUtils { /** * If set to "1", we enable the verbose logging. * * <p> * (See also InitLogging() in http://ac/system/libbase/logging.cpp) */ public static final boolean RAVENWOOD_VERBOSE_LOGGING = Loading @@ -69,21 +71,24 @@ public class RavenwoodInternalUtils { return sEnableExtraRuntimeCheck; } /** Simple logging method. */ /** * Simple logging method. */ public static void log(String tag, String message) { // Avoid using Android's Log class, which could be broken for various reasons. // (e.g. the JNI file doesn't exist for whatever reason) System.out.print(tag + ": " + message + "\n"); } /** Simple logging method. */ /** * Simple logging method. */ private void log(String tag, String format, Object... args) { log(tag, String.format(format, args)); } /** * Internal implementation of * {@link android.platform.test.ravenwood.RavenwoodRule#loadJniLibrary(String)} * Internal implementation of {@link android.platform.test.ravenwood.RavenwoodRule#loadJniLibrary(String)} */ public static void loadJniLibrary(String libname) { if (isOnRavenwood()) { Loading Loading @@ -171,14 +176,16 @@ public class RavenwoodInternalUtils { /** * @return the full directory path that contains the "ravenwood-runtime" files. * * <p> * This method throws if called on the device side. */ public static String getRavenwoodRuntimePath() { return RavenwoodHelper.getRavenwoodRuntimePath(); } /** Close an {@link AutoCloseable}. */ /** * Close an {@link AutoCloseable}. */ public static void closeQuietly(AutoCloseable c) { if (c != null) { try { Loading @@ -189,7 +196,9 @@ public class RavenwoodInternalUtils { } } /** Close a {@link FileDescriptor}. */ /** * Close a {@link FileDescriptor}. */ public static void closeQuietly(FileDescriptor fd) { var is = new FileInputStream(fd); closeQuietly(is); Loading Loading @@ -222,7 +231,7 @@ public class RavenwoodInternalUtils { /** * Run a supplier and swallow the exception, if any. * * <p> * It's a dangerous function. Only use it in an exception handler where we don't want to crash. */ @Nullable Loading @@ -237,7 +246,7 @@ public class RavenwoodInternalUtils { /** * Run a runnable and swallow the exception, if any. * * <p> * It's a dangerous function. Only use it in an exception handler where we don't want to crash. */ public static void runIgnoringException(@NonNull Runnable r) { Loading @@ -255,7 +264,9 @@ public class RavenwoodInternalUtils { return stringWriter.toString(); } /** Same as {@link Integer#parseInt(String)} but accepts null and returns null. */ /** * Same as {@link Integer#parseInt(String)} but accepts null and returns null. */ @Nullable public static Integer parseNullableInt(@Nullable String value) { if (value == null) { Loading @@ -278,7 +289,7 @@ public class RavenwoodInternalUtils { /** * Convert a wildcard string using "*" and "**" to match Java classnames. * * <p> * The input string can only contain alnum, "$", "." and "*". */ public static Pattern parseClassNameWildcard(String pattern) { Loading @@ -299,4 +310,43 @@ public class RavenwoodInternalUtils { temp = temp.replace("@@", ".*"); return Pattern.compile(temp); } /** * @return true if it's a junit4 test method. */ public static Boolean isTestMethod(@NonNull Class<?> clazz, @NonNull Method method) { return method.getAnnotation(org.junit.Test.class) != null; } private static final Pattern sParamsPattern = Pattern.compile("\\[.*$"); /** * Take a {@link Description#getMethodName()} result and extract the "real" method name, * i.e. without the parameters. */ public static String getMethodNameFromMethodDescription(@NonNull String methodDescriptionName) { return sParamsPattern.matcher(methodDescriptionName).replaceFirst(""); } /** * @return a canonical name of a test method. {@code clazz} may be a subclass of * {@link Method#getDeclaringClass()}. */ public static String toCanonicalTestName(@NonNull Class<?> clazz, @NonNull Method method) { return clazz.getName() + "#" + method.getName(); } /** * @return a canonical name of a test method. */ public static String toCanonicalTestName(@NonNull Description description) { if (!description.isTest()) { // It's a test class. return description.getClassName(); } else { // It's a test method return description.getClassName() + "#" + getMethodNameFromMethodDescription( description.getMethodName()); } } } No newline at end of file
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java +96 −12 Original line number Diff line number Diff line Loading @@ -15,7 +15,9 @@ */ package android.platform.test.ravenwood; import static com.android.ravenwood.common.RavenwoodInternalUtils.isTestMethod; import static com.android.ravenwood.common.RavenwoodInternalUtils.parseClassNameWildcard; import static com.android.ravenwood.common.RavenwoodInternalUtils.toCanonicalTestName; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -54,6 +56,11 @@ import java.util.regex.Pattern; * * This feature is only used locally. On CI server, we never use it. * * - You can override the above logic and run exact test classes / test methods by * setting a case-insensitive regex to $RAVENWOOD_FORCE_FILTER_REGEX. If it's set, * only the tests that are mathing it would be executed. For example, * running with RAVENWOOD_FORCE_FILTER_REGEX='(MyTestClass1|MyTestClass2#testFoo)' would * execute all tests in MyTestClass1, and only testFoo() in MyTestClass2. */ public abstract class RavenwoodEnablementChecker { private static final String TAG = RavenwoodInternalUtils.TAG; Loading Loading @@ -161,20 +168,26 @@ public abstract class RavenwoodEnablementChecker { */ @VisibleForTesting public static void setDefaultInstance() { sInstance = new RavenwoodEnablementCheckerImpl(getRunMode(), getPolicyFiles()); sInstance = new RavenwoodEnablementCheckerImpl(getRunMode(), getPolicyFiles(), RavenwoodEnvironment.getInstance().getEnvVar("RAVENWOOD_FORCE_FILTER_REGEX", null)); } /** * Force set a checker for testing. */ @VisibleForTesting public static void overrideInstance(@NonNull RunMode runMode, @Nullable String policyText) { public static void overrideInstance( @NonNull RunMode runMode, @Nullable String policyText, @Nullable String overridingPattern ) { try { var parser = new EnablementTextPolicyParser(); if (policyText != null && !policyText.isEmpty()) { parser.parse("[in-memory]", policyText); } sInstance = new RavenwoodEnablementCheckerImpl(runMode, parser.getResult()); sInstance = new RavenwoodEnablementCheckerImpl( runMode, parser.getResult(), overridingPattern); } catch (IOException e) { SneakyThrow.sneakyThrow(e); // IOException shouldn't happen, but just in case } Loading @@ -193,24 +206,39 @@ public abstract class RavenwoodEnablementChecker { /** * Actual logic. This combines the annotation policy with the text policy. */ @VisibleForTesting public static class RavenwoodEnablementCheckerImpl extends RavenwoodEnablementChecker { static class RavenwoodEnablementCheckerImpl extends RavenwoodEnablementChecker { private final RunMode mRunMode; private final PolicyChecker mChecker; public RavenwoodEnablementCheckerImpl(RunMode runMode, String[] policyFiles) { this(runMode, EnablementTextPolicyParser.parsePolicyFiles(policyFiles)); RavenwoodEnablementCheckerImpl( @NonNull RunMode runMode, @NonNull String[] policyFiles, @Nullable String overridingPattern) { this(runMode, EnablementTextPolicyParser.parsePolicyFiles(policyFiles), overridingPattern); } RavenwoodEnablementCheckerImpl(RunMode runMode, PolicyChecker subChecker) { RavenwoodEnablementCheckerImpl( @NonNull RunMode runMode, @NonNull PolicyChecker subChecker, @Nullable String overridingPattern) { this.mRunMode = runMode; var chain = new PolicyCheckerChain(); if (overridingPattern == null || overridingPattern.isEmpty()) { // Annotations always win. chain.add(new AnnotationPolicyChecker()); // Text policy changes the default behavior. chain.add(subChecker); } else { // Use a regex-based filter, and only run the exact matching tests. chain.add(new RegexRunFilter( Pattern.compile(overridingPattern, Pattern.CASE_INSENSITIVE), RunPolicy.Enabled, RunPolicy.NeverRun )); } mChecker = chain; } Loading Loading @@ -592,3 +620,59 @@ class EnablementTextPolicyParser { return true; } } /** * {@link PolicyChecker} based on a {@link Pattern}. */ class RegexRunFilter implements PolicyChecker { private static final String TAG = "RegexRunFilter"; private final Pattern mRegex; private final RunPolicy mMatchingPolicy; private final RunPolicy mNonMatchingPolicy; /** * Constructor. * * @param regex regex to match. We apply it on both classes and methods. * @param matchingPolicy policy for matching items * @param nonMatchingPolicy policy for non-matching items */ public RegexRunFilter( @NonNull Pattern regex, @NonNull RunPolicy matchingPolicy, @NonNull RunPolicy nonMatchingPolicy) { mRegex = regex; mMatchingPolicy = matchingPolicy; mNonMatchingPolicy = nonMatchingPolicy; } private boolean classMatches(Class<?> testClass) { return mRegex.matcher(testClass.getName()).find(); } @Override public RunPolicy getClassPolicy(Class<?> testClass) { if (classMatches(testClass)) { return mMatchingPolicy; } // If the class has any test matching test method, still return "enabled". // (but unmatched methods would be "unspecified".) for (var m : testClass.getMethods()) { if (!isTestMethod(testClass, m)) { continue; } if (mRegex.matcher(toCanonicalTestName(testClass, m)).find()) { return mMatchingPolicy; } } return mNonMatchingPolicy; } @Override public RunPolicy getMethodPolicy(Description description) { if (mRegex.matcher(toCanonicalTestName(description)).find()) { return mMatchingPolicy; } return classMatches(description.getTestClass()) ? mMatchingPolicy : mNonMatchingPolicy; } }
ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodEnablementTest.java +112 −2 Original line number Diff line number Diff line Loading @@ -308,8 +308,6 @@ public class RavenwoodEnablementTest extends RavenwoodRunnerTestBase { } } //-- @RunWith(BlockJUnit4ClassRunner.class) // CHECKSTYLE:OFF Generated code @Expected(value = """ Loading Loading @@ -504,4 +502,116 @@ public class RavenwoodEnablementTest extends RavenwoodRunnerTestBase { public void test3() { } } @RunWith(BlockJUnit4ClassRunner.class) // CHECKSTYLE:OFF Generated code @Expected(value = """ testRunStarted: classes testSuiteStarted: classes testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel testStarted: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testAssumptionFailure: got: <false>, expected: is <true> testFinished: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testStarted: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testAssumptionFailure: got: <false>, expected: is <true> testFinished: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testStarted: test3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testFinished: test3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel) testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_methodLevel testSuiteFinished: classes testRunFinished: 3,0,2,0 """,runMode = RunMode.AlsoDisabledTests, enablementPolicy = """ !module RavenwoodCoreTest ** false # disable all classes """, overridingRegex="testwith.*#test3" ) // CHECKSTYLE:ON public static class TestWithPolicyWithForceRegex_methodLevel { public TestWithPolicyWithForceRegex_methodLevel() { } @Test public void test1() { } @Test public void test2() { } @Test @DisabledOnRavenwood public void test3() { } } @RunWith(BlockJUnit4ClassRunner.class) // CHECKSTYLE:OFF Generated code @Expected(value = """ testRunStarted: classes testSuiteStarted: classes testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled testStarted: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testFinished: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testStarted: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testFinished: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testStarted: test3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testFinished: test3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled) testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelEnabled testSuiteFinished: classes testRunFinished: 3,0,0,0 """,runMode = RunMode.AlsoDisabledTests, enablementPolicy = """ !module RavenwoodCoreTest ** false # disable all classes """, overridingRegex="TestWithPolicy" // Should run all the tests ) // CHECKSTYLE:ON public static class TestWithPolicyWithForceRegex_classLevelEnabled { public TestWithPolicyWithForceRegex_classLevelEnabled() { } @Test public void test1() { } @Test public void test2() { } @Test @DisabledOnRavenwood public void test3() { } } @RunWith(BlockJUnit4ClassRunner.class) // CHECKSTYLE:OFF Generated code @Expected(value = """ testRunStarted: classes testSuiteStarted: classes testSuiteStarted: <init>(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelDisabled) testIgnored: <init>(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelDisabled) testSuiteFinished: <init>(com.android.ravenwoodtest.runnercallbacktests.RavenwoodEnablementTest$TestWithPolicyWithForceRegex_classLevelDisabled) testSuiteFinished: classes testRunFinished: 0,0,0,1 """,runMode = RunMode.AlsoDisabledTests, enablementPolicy = """ """, overridingRegex="NotMatchingAnyTests" // no tests should be executed ) // CHECKSTYLE:ON public static class TestWithPolicyWithForceRegex_classLevelDisabled { public TestWithPolicyWithForceRegex_classLevelDisabled() { } @Test public void test1() { } @Test public void test2() { } @Test @DisabledOnRavenwood public void test3() { } } }
ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java +2 −1 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ public abstract class RavenwoodRunnerTestBase { String value(); RunMode runMode() default RunMode.Normal; String enablementPolicy() default ""; String overridingRegex() default ""; } private static final AtomicReference<Throwable> sError = new AtomicReference<>(); Loading Loading @@ -153,7 +154,7 @@ public abstract class RavenwoodRunnerTestBase { // Oevrride enablement policy RavenwoodEnablementChecker.overrideInstance( expected.runMode(), expected.enablementPolicy()); expected.runMode(), expected.enablementPolicy(), expected.overridingRegex()); sError.set(null); Loading