Loading ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java +115 −2 Original line number Diff line number Diff line Loading @@ -23,9 +23,15 @@ import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.annotations.EnabledOnRavenwood; import com.android.ravenwood.common.RavenwoodInternalUtils; import com.android.ravenwood.common.SneakyThrow; import org.junit.runner.Description; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; Loading Loading @@ -75,6 +81,89 @@ public class RavenwoodEnablementChecker { public static volatile Pattern REALLY_DISABLED_PATTERN = Pattern.compile( Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLED"), "")); /** * When using RAVENWOOD_TEST_ENABLEMENT_POLICY, you can provide an external policy text file * to change whether each test classes or methods are enabled in Ravenwood without the need * to use {@link DisabledOnRavenwood} or {@link EnabledOnRavenwood} annotations. * * The policy file are lines, each with 2 space delimited fields in the following format: * * [signature: string] [enabled: boolean] * * The "signature" field has 2 subcomponents: a class name and method name, separated with the * '#' symbol (e.g. com.example.TestClass#testMethod). Method name can be omitted if the * policy in question applies to the entire class, not a specific method. * * When the "signature" field is the special value "*", the "enable" field sets the * global default enablement status. */ private static final String RAVENWOOD_TEST_ENABLEMENT_POLICY = System.getenv("RAVENWOOD_TEST_ENABLEMENT_POLICY"); private static final EnablementPolicy sEnablementPolicy = new EnablementPolicy(); private static class ClassEnablementPolicy { Boolean mEnabled; Map<String, Boolean> mMethods; } private static class EnablementPolicy { boolean mEnabled = true; final Map<String, ClassEnablementPolicy> mClasses = new HashMap<>(); boolean shouldEnableClass(String className) { if (mClasses.isEmpty()) { return mEnabled; } var clazz = mClasses.get(className); if (clazz == null) { return mEnabled; } return clazz.mEnabled != null ? clazz.mEnabled : mEnabled; } Boolean shouldEnableMethod(String className, String methodName) { if (mClasses.isEmpty()) { return null; } var clazz = mClasses.get(className); if (clazz == null) { return null; } if (clazz.mMethods == null) { return null; } return clazz.mMethods.get(methodName); } void parseLine(String line) { var columns = line.split("\\s", 2); if (columns.length != 2) return; var signature = columns[0]; boolean enable = Boolean.parseBoolean(columns[1]); if (signature.equals("*")) { // Setting the global default policy mEnabled = enable; } else { var s = signature.split("\\#"); var clazz = s[0]; var method = s.length > 1 ? s[1] : null; var policy = mClasses.computeIfAbsent(clazz, k -> new ClassEnablementPolicy()); if (method != null) { if (policy.mMethods == null) policy.mMethods = new HashMap<>(); policy.mMethods.put(method, enable); } else { policy.mEnabled = enable; } } } void clear() { mEnabled = true; mClasses.clear(); } } static { if (RUN_DISABLED_TESTS) { log(TAG, "$RAVENWOOD_RUN_DISABLED_TESTS enabled: running only disabled tests"); Loading @@ -82,11 +171,30 @@ public class RavenwoodEnablementChecker { log(TAG, "$RAVENWOOD_REALLY_DISABLED=" + REALLY_DISABLED_PATTERN.pattern()); } } if (RAVENWOOD_TEST_ENABLEMENT_POLICY != null) { try { var policy = Files.readString(Path.of(RAVENWOOD_TEST_ENABLEMENT_POLICY)); setTestEnablementPolicy(policy); } catch (IOException e) { SneakyThrow.sneakyThrow(e); } } } private RavenwoodEnablementChecker() { } public static void setTestEnablementPolicy(String policy) { sEnablementPolicy.clear(); policy.lines().map(String::strip) // Remove inline comments .map(line -> line.replaceAll("\\s+\\#.*$", "").strip()) // Ignore empty lines and full line comments .filter(line -> !line.isEmpty() && !line.startsWith("#")) .forEach(sEnablementPolicy::parseLine); } /** * Determine if the given {@link Description} should be enabled when running on the * Ravenwood test environment. Loading @@ -105,6 +213,9 @@ public class RavenwoodEnablementChecker { result = true; } else if (description.getAnnotation(DisabledOnRavenwood.class) != null) { result = false; } else { result = sEnablementPolicy.shouldEnableMethod( description.getClassName(), description.getMethodName()); } if (result != null) { if (checkRunDisabledTestsFlag && RUN_DISABLED_TESTS) { Loading @@ -125,11 +236,13 @@ public class RavenwoodEnablementChecker { public static boolean shouldRunClassOnRavenwood(@NonNull Class<?> testClass, boolean checkRunDisabledTestsFlag) { boolean result = true; boolean result; if (testClass.getAnnotation(EnabledOnRavenwood.class) != null) { result = true; } else if (testClass.getAnnotation(DisabledOnRavenwood.class) != null) { result = false; } else { result = sEnablementPolicy.shouldEnableClass(testClass.getName()); } if (checkRunDisabledTestsFlag && RUN_DISABLED_TESTS) { // Invert the result + check the really disable pattern Loading @@ -144,7 +257,7 @@ public class RavenwoodEnablementChecker { * * This only works on tests, not on classes. */ static boolean shouldReallyDisableTest(@NonNull Class<?> testClass, private static boolean shouldReallyDisableTest(@NonNull Class<?> testClass, @Nullable String methodName) { if (REALLY_DISABLED_PATTERN.pattern().isEmpty()) { return false; Loading ravenwood/junit-src/android/platform/test/annotations/DisabledOnRavenwood.java +2 −4 Original line number Diff line number Diff line Loading @@ -29,10 +29,8 @@ import java.lang.annotation.Target; * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over * an {@link DisabledOnRavenwood} annotation. * * This annotation only takes effect when the containing class has a {@code * RavenwoodRule} or {@code RavenwoodClassRule} configured. Ignoring is accomplished by * throwing an {@code org.junit.AssumptionViolatedException} which test infrastructure treats as * being ignored. * Ignoring is accomplished by throwing an {@code org.junit.AssumptionViolatedException} which * test infrastructure treats as being ignored. * * This annotation has no effect on any other non-Ravenwood test environments. * Loading ravenwood/junit-src/android/platform/test/annotations/EnabledOnRavenwood.java +2 −4 Original line number Diff line number Diff line Loading @@ -29,10 +29,8 @@ import java.lang.annotation.Target; * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over * an {@link DisabledOnRavenwood} annotation. * * This annotation only takes effect when the containing class has a {@code * RavenwoodRule} or {@code RavenwoodClassRule} configured. Ignoring is accomplished by * throwing an {@code org.junit.AssumptionViolatedException} which test infrastructure treats as * being ignored. * Ignoring is accomplished by throwing an {@code org.junit.AssumptionViolatedException} which * test infrastructure treats as being ignored. * * This annotation has no effect on any other non-Ravenwood test environments. * Loading ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodEnablementTest.java +83 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static org.junit.Assert.fail; import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.annotations.EnabledOnRavenwood; import android.platform.test.annotations.NoRavenizer; import android.platform.test.ravenwood.RavenwoodEnablementChecker; import androidx.test.ext.junit.runners.AndroidJUnit4; Loading @@ -38,6 +39,28 @@ import java.util.regex.Pattern; @NoRavenizer public class RavenwoodEnablementTest extends RavenwoodRunnerTestBase { private static final String ENABLEMENT_POLICY = """ # Enable all tests by default * true # inline comments should work # Disable only the method TestPolicy#testDisabled com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy#testDisabled false # Disable the entire TestPolicyDisableClass class com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicyDisableClass false """; @BeforeClass public static void beforeClass() { RavenwoodEnablementChecker.setTestEnablementPolicy(ENABLEMENT_POLICY); } @AfterClass public static void afterClass() { // Clear the test enablement policy RavenwoodEnablementChecker.setTestEnablementPolicy(""); } @RunWith(AndroidJUnit4.class) // CHECKSTYLE:OFF @Expected(""" Loading Loading @@ -224,4 +247,64 @@ public class RavenwoodEnablementTest extends RavenwoodRunnerTestBase { fail("This should not run"); } } @RunWith(AndroidJUnit4.class) // CHECKSTYLE:OFF @Expected(""" testRunStarted: classes testSuiteStarted: classes testSuiteStarted: com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy testStarted: testEnabled(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testFinished: testEnabled(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testStarted: testDisabled(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testAssumptionFailure: got: <false>, expected: is <true> testFinished: testDisabled(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testStarted: testDisabledByAnnotation(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testAssumptionFailure: got: <false>, expected: is <true> testFinished: testDisabledByAnnotation(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testSuiteFinished: com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy testSuiteFinished: classes testRunFinished: 3,0,2,0 """) // CHECKSTYLE:ON public static class TestPolicy { @Test public void testDisabled() { fail("This should be disabled by policy file"); } @Test @DisabledOnRavenwood public void testDisabledByAnnotation() { fail("This should be disabled by policy file"); } @Test public void testEnabled() { } } @RunWith(AndroidJUnit4.class) //CHECKSTYLE:OFF @Expected(""" testRunStarted: classes testSuiteStarted: classes testSuiteStarted: TestPolicyDisableClass(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicyDisableClass) testIgnored: TestPolicyDisableClass(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicyDisableClass) testSuiteFinished: TestPolicyDisableClass(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicyDisableClass) testSuiteFinished: classes testRunFinished: 0,0,0,1 """) //CHECKSTYLE:ON public static class TestPolicyDisableClass { @Test public void testDisabled() { fail("This should be disabled by policy file"); } @Test public void testEnabled() { fail("This should be disabled by policy file"); } } } Loading
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java +115 −2 Original line number Diff line number Diff line Loading @@ -23,9 +23,15 @@ import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.annotations.EnabledOnRavenwood; import com.android.ravenwood.common.RavenwoodInternalUtils; import com.android.ravenwood.common.SneakyThrow; import org.junit.runner.Description; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; Loading Loading @@ -75,6 +81,89 @@ public class RavenwoodEnablementChecker { public static volatile Pattern REALLY_DISABLED_PATTERN = Pattern.compile( Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLED"), "")); /** * When using RAVENWOOD_TEST_ENABLEMENT_POLICY, you can provide an external policy text file * to change whether each test classes or methods are enabled in Ravenwood without the need * to use {@link DisabledOnRavenwood} or {@link EnabledOnRavenwood} annotations. * * The policy file are lines, each with 2 space delimited fields in the following format: * * [signature: string] [enabled: boolean] * * The "signature" field has 2 subcomponents: a class name and method name, separated with the * '#' symbol (e.g. com.example.TestClass#testMethod). Method name can be omitted if the * policy in question applies to the entire class, not a specific method. * * When the "signature" field is the special value "*", the "enable" field sets the * global default enablement status. */ private static final String RAVENWOOD_TEST_ENABLEMENT_POLICY = System.getenv("RAVENWOOD_TEST_ENABLEMENT_POLICY"); private static final EnablementPolicy sEnablementPolicy = new EnablementPolicy(); private static class ClassEnablementPolicy { Boolean mEnabled; Map<String, Boolean> mMethods; } private static class EnablementPolicy { boolean mEnabled = true; final Map<String, ClassEnablementPolicy> mClasses = new HashMap<>(); boolean shouldEnableClass(String className) { if (mClasses.isEmpty()) { return mEnabled; } var clazz = mClasses.get(className); if (clazz == null) { return mEnabled; } return clazz.mEnabled != null ? clazz.mEnabled : mEnabled; } Boolean shouldEnableMethod(String className, String methodName) { if (mClasses.isEmpty()) { return null; } var clazz = mClasses.get(className); if (clazz == null) { return null; } if (clazz.mMethods == null) { return null; } return clazz.mMethods.get(methodName); } void parseLine(String line) { var columns = line.split("\\s", 2); if (columns.length != 2) return; var signature = columns[0]; boolean enable = Boolean.parseBoolean(columns[1]); if (signature.equals("*")) { // Setting the global default policy mEnabled = enable; } else { var s = signature.split("\\#"); var clazz = s[0]; var method = s.length > 1 ? s[1] : null; var policy = mClasses.computeIfAbsent(clazz, k -> new ClassEnablementPolicy()); if (method != null) { if (policy.mMethods == null) policy.mMethods = new HashMap<>(); policy.mMethods.put(method, enable); } else { policy.mEnabled = enable; } } } void clear() { mEnabled = true; mClasses.clear(); } } static { if (RUN_DISABLED_TESTS) { log(TAG, "$RAVENWOOD_RUN_DISABLED_TESTS enabled: running only disabled tests"); Loading @@ -82,11 +171,30 @@ public class RavenwoodEnablementChecker { log(TAG, "$RAVENWOOD_REALLY_DISABLED=" + REALLY_DISABLED_PATTERN.pattern()); } } if (RAVENWOOD_TEST_ENABLEMENT_POLICY != null) { try { var policy = Files.readString(Path.of(RAVENWOOD_TEST_ENABLEMENT_POLICY)); setTestEnablementPolicy(policy); } catch (IOException e) { SneakyThrow.sneakyThrow(e); } } } private RavenwoodEnablementChecker() { } public static void setTestEnablementPolicy(String policy) { sEnablementPolicy.clear(); policy.lines().map(String::strip) // Remove inline comments .map(line -> line.replaceAll("\\s+\\#.*$", "").strip()) // Ignore empty lines and full line comments .filter(line -> !line.isEmpty() && !line.startsWith("#")) .forEach(sEnablementPolicy::parseLine); } /** * Determine if the given {@link Description} should be enabled when running on the * Ravenwood test environment. Loading @@ -105,6 +213,9 @@ public class RavenwoodEnablementChecker { result = true; } else if (description.getAnnotation(DisabledOnRavenwood.class) != null) { result = false; } else { result = sEnablementPolicy.shouldEnableMethod( description.getClassName(), description.getMethodName()); } if (result != null) { if (checkRunDisabledTestsFlag && RUN_DISABLED_TESTS) { Loading @@ -125,11 +236,13 @@ public class RavenwoodEnablementChecker { public static boolean shouldRunClassOnRavenwood(@NonNull Class<?> testClass, boolean checkRunDisabledTestsFlag) { boolean result = true; boolean result; if (testClass.getAnnotation(EnabledOnRavenwood.class) != null) { result = true; } else if (testClass.getAnnotation(DisabledOnRavenwood.class) != null) { result = false; } else { result = sEnablementPolicy.shouldEnableClass(testClass.getName()); } if (checkRunDisabledTestsFlag && RUN_DISABLED_TESTS) { // Invert the result + check the really disable pattern Loading @@ -144,7 +257,7 @@ public class RavenwoodEnablementChecker { * * This only works on tests, not on classes. */ static boolean shouldReallyDisableTest(@NonNull Class<?> testClass, private static boolean shouldReallyDisableTest(@NonNull Class<?> testClass, @Nullable String methodName) { if (REALLY_DISABLED_PATTERN.pattern().isEmpty()) { return false; Loading
ravenwood/junit-src/android/platform/test/annotations/DisabledOnRavenwood.java +2 −4 Original line number Diff line number Diff line Loading @@ -29,10 +29,8 @@ import java.lang.annotation.Target; * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over * an {@link DisabledOnRavenwood} annotation. * * This annotation only takes effect when the containing class has a {@code * RavenwoodRule} or {@code RavenwoodClassRule} configured. Ignoring is accomplished by * throwing an {@code org.junit.AssumptionViolatedException} which test infrastructure treats as * being ignored. * Ignoring is accomplished by throwing an {@code org.junit.AssumptionViolatedException} which * test infrastructure treats as being ignored. * * This annotation has no effect on any other non-Ravenwood test environments. * Loading
ravenwood/junit-src/android/platform/test/annotations/EnabledOnRavenwood.java +2 −4 Original line number Diff line number Diff line Loading @@ -29,10 +29,8 @@ import java.lang.annotation.Target; * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over * an {@link DisabledOnRavenwood} annotation. * * This annotation only takes effect when the containing class has a {@code * RavenwoodRule} or {@code RavenwoodClassRule} configured. Ignoring is accomplished by * throwing an {@code org.junit.AssumptionViolatedException} which test infrastructure treats as * being ignored. * Ignoring is accomplished by throwing an {@code org.junit.AssumptionViolatedException} which * test infrastructure treats as being ignored. * * This annotation has no effect on any other non-Ravenwood test environments. * Loading
ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodEnablementTest.java +83 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static org.junit.Assert.fail; import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.annotations.EnabledOnRavenwood; import android.platform.test.annotations.NoRavenizer; import android.platform.test.ravenwood.RavenwoodEnablementChecker; import androidx.test.ext.junit.runners.AndroidJUnit4; Loading @@ -38,6 +39,28 @@ import java.util.regex.Pattern; @NoRavenizer public class RavenwoodEnablementTest extends RavenwoodRunnerTestBase { private static final String ENABLEMENT_POLICY = """ # Enable all tests by default * true # inline comments should work # Disable only the method TestPolicy#testDisabled com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy#testDisabled false # Disable the entire TestPolicyDisableClass class com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicyDisableClass false """; @BeforeClass public static void beforeClass() { RavenwoodEnablementChecker.setTestEnablementPolicy(ENABLEMENT_POLICY); } @AfterClass public static void afterClass() { // Clear the test enablement policy RavenwoodEnablementChecker.setTestEnablementPolicy(""); } @RunWith(AndroidJUnit4.class) // CHECKSTYLE:OFF @Expected(""" Loading Loading @@ -224,4 +247,64 @@ public class RavenwoodEnablementTest extends RavenwoodRunnerTestBase { fail("This should not run"); } } @RunWith(AndroidJUnit4.class) // CHECKSTYLE:OFF @Expected(""" testRunStarted: classes testSuiteStarted: classes testSuiteStarted: com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy testStarted: testEnabled(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testFinished: testEnabled(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testStarted: testDisabled(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testAssumptionFailure: got: <false>, expected: is <true> testFinished: testDisabled(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testStarted: testDisabledByAnnotation(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testAssumptionFailure: got: <false>, expected: is <true> testFinished: testDisabledByAnnotation(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy) testSuiteFinished: com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicy testSuiteFinished: classes testRunFinished: 3,0,2,0 """) // CHECKSTYLE:ON public static class TestPolicy { @Test public void testDisabled() { fail("This should be disabled by policy file"); } @Test @DisabledOnRavenwood public void testDisabledByAnnotation() { fail("This should be disabled by policy file"); } @Test public void testEnabled() { } } @RunWith(AndroidJUnit4.class) //CHECKSTYLE:OFF @Expected(""" testRunStarted: classes testSuiteStarted: classes testSuiteStarted: TestPolicyDisableClass(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicyDisableClass) testIgnored: TestPolicyDisableClass(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicyDisableClass) testSuiteFinished: TestPolicyDisableClass(com.android.ravenwoodtest.coretest.RavenwoodEnablementTest$TestPolicyDisableClass) testSuiteFinished: classes testRunFinished: 0,0,0,1 """) //CHECKSTYLE:ON public static class TestPolicyDisableClass { @Test public void testDisabled() { fail("This should be disabled by policy file"); } @Test public void testEnabled() { fail("This should be disabled by policy file"); } } }