Define `SystemUiRavenTests`, default to "ignored."
With our recent foundational work, we're now in a position to define
a `SystemUiRavenTests` target that runs all `multivalentTests`. This
change uses the `@DisabledOnRavenwood` annotation to "ignore" all
tests by default when executed under the Ravenwood environment, and
future changes will use `@EnabledOnRavenwood` to progressively
opt-in tests that are passing. (Tests continue running as-is under
all other environments.)
As a reminder, our strategy of using a `RavenwoodRule` and
`RavenwoodClassRule` to integrate with JUnit means that all test
classes <clinit> will be run, even if all contained tests will end
up being ignored. We've done a few passes of using `by lazy` and
`lateinit` to ensure that we avoid interacting with Ravenwood
unsupported APIs, but once this change lands it will require that
future test updates remain vigilant to keep presubmit green. This
aligns with the SysUI testing policy outlined in <go/no-static-work>,
but that policy will now be more directly enforced.
Bug: 319647875
Test: atest SystemUiRoboTests
Test: atest SystemUiRavenTests
Change-Id: I5ef7a2ada3047d468ff335511467f9691752e9f1
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 80656e9..90593d6 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -330,6 +330,7 @@
"androidx.core_core-animation-testing-nodeps",
"androidx.compose.ui_ui",
"flag-junit",
+ "ravenwood-junit",
"platform-test-annotations",
"notification_flags_lib",
],
@@ -386,7 +387,8 @@
android_app {
name: "SystemUIRobo-stub",
- use_resource_processor: true,
+ // SystemUiRavenTests references the .aapt.srcjar
+ use_resource_processor: false,
defaults: [
"platform_app_defaults",
"SystemUI_optimized_defaults",
@@ -458,6 +460,34 @@
],
}
+android_ravenwood_test {
+ name: "SystemUiRavenTests",
+ srcs: [
+ ":SystemUI-tests-utils",
+ ":SystemUI-tests-multivalent",
+ // TODO(b/294256649): pivot to using {.aapt.jar} and re-enable
+ // use_resource_processor: true when better supported by soong
+ ":SystemUIRobo-stub{.aapt.srcjar}",
+ ],
+ static_libs: [
+ "SystemUI-core",
+ "SystemUI-res",
+ "SystemUI-tests-base",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.core_core-animation-testing",
+ "androidx.test.ext.junit",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+ auto_gen_config: true,
+ plugins: [
+ "dagger2-compiler",
+ ],
+}
+
// Opt-out config for optimizing the SystemUI target using R8.
// Disabled via `export SYSTEMUI_OPTIMIZE_JAVA=false`, or explicitly in Make via
// `SYSTEMUI_OPTIMIZE_JAVA := false`.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 7274c0c..4528957 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -31,6 +31,7 @@
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -66,8 +67,11 @@
@Mock
private SystemUIDialog.Delegate mDelegate;
+ // TODO(b/292141694): build out Ravenwood support for DeviceFlagsValueProvider
+ // Ravenwood already has solid support for SetFlagsRule, but CheckFlagsRule will be added soon
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood() ? null
+ : DeviceFlagsValueProvider.createCheckFlagsRule();
@Before
public void setup() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index af7f4c8..b62b3a2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -21,17 +21,25 @@
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
+import android.content.Context;
+import android.content.res.Resources;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.MessageQueue;
import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.ravenwood.RavenwoodClassRule;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.test.mock.MockContext;
import android.testing.DexmakerShareClassLoaderRule;
import android.testing.LeakCheck;
import android.testing.TestWithLooperRule;
import android.testing.TestableLooper;
import android.util.Log;
+import android.util.Singleton;
import androidx.annotation.NonNull;
import androidx.core.animation.AndroidXAnimatorIsolationRule;
@@ -44,17 +52,24 @@
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.ClassRule;
import org.junit.Rule;
import org.mockito.Mockito;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
import java.util.concurrent.Future;
/**
* Base class that does System UI specific setup.
*/
+// NOTE: This @DisabledOnRavenwood annotation is inherited to all subclasses (unless overridden
+// via a more-specific @EnabledOnRavenwood annotation); this means that by default all
+// subclasses will be "ignored" when executed on the Ravenwood testing environment; more
+// background on Ravenwood is available at go/ravenwood-docs
+@DisabledOnRavenwood
public abstract class SysuiTestCase {
private static final String TAG = "SysuiTestCase";
@@ -66,6 +81,23 @@
public AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule =
new AndroidXAnimatorIsolationRule();
+ /**
+ * Rule that respects class-level annotations such as {@code @DisabledOnRavenwood} when tests
+ * are running on Ravenwood; on all other test environments this rule is a no-op passthrough.
+ */
+ @ClassRule(order = Integer.MIN_VALUE + 1)
+ public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule();
+
+ /**
+ * Rule that defines and prepares the Ravenwood environment when tests are running on
+ * Ravenwood; on all other test environments this rule is a no-op passthrough.
+ */
+ @Rule(order = Integer.MIN_VALUE + 1)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProcessApp()
+ .setProvideMainThread(true)
+ .build();
+
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -78,7 +110,7 @@
@NonNull
private SysuiTestableContext createTestableContext() {
SysuiTestableContext context = new SysuiTestableContext(
- InstrumentationRegistry.getContext(), getLeakCheck());
+ getTestableContextBase(), getLeakCheck());
if (isRobolectricTest()) {
// Manually associate a Display to context for Robolectric test. Similar to b/214297409
return context.createDefaultDisplayContext();
@@ -87,6 +119,43 @@
}
}
+ @NonNull
+ private Context getTestableContextBase() {
+ if (isRavenwoodTest()) {
+ // TODO(b/292141694): build out Ravenwood support for Context
+ // Ravenwood doesn't yet provide a Context, but many SysUI tests assume one exists;
+ // so here we construct just enough of a Context to be useful; this will be replaced
+ // as more of the Ravenwood environment is built out
+ return new MockContext() {
+ @Override
+ public void setTheme(int resid) {
+ // TODO(b/318393625): build out Ravenwood support for Resources
+ // until then, ignored as no-op
+ }
+
+ @Override
+ public Resources getResources() {
+ // TODO(b/318393625): build out Ravenwood support for Resources
+ return Mockito.mock(Resources.class);
+ }
+
+ private Singleton<Executor> mMainExecutor = new Singleton<>() {
+ @Override
+ protected Executor create() {
+ return new HandlerExecutor(new Handler(Looper.getMainLooper()));
+ }
+ };
+
+ @Override
+ public Executor getMainExecutor() {
+ return mMainExecutor.get();
+ }
+ };
+ } else {
+ return InstrumentationRegistry.getContext();
+ }
+ }
+
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
@@ -103,17 +172,22 @@
public void SysuiSetup() throws Exception {
mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
mDependency = mSysuiDependency.install();
- mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
- Instrumentation inst = spy(mRealInstrumentation);
- when(inst.getContext()).thenAnswer(invocation -> {
- throw new RuntimeException(
- "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
- });
- when(inst.getTargetContext()).thenAnswer(invocation -> {
- throw new RuntimeException(
- "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
- });
- InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
+ // TODO(b/292141694): build out Ravenwood support for Instrumentation
+ // Ravenwood doesn't yet provide Instrumentation, so we sidestep this global configuration
+ // step; any tests that rely on it are already being excluded on Ravenwood
+ if (!isRavenwoodTest()) {
+ mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
+ Instrumentation inst = spy(mRealInstrumentation);
+ when(inst.getContext()).thenAnswer(invocation -> {
+ throw new RuntimeException(
+ "Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
+ });
+ when(inst.getTargetContext()).thenAnswer(invocation -> {
+ throw new RuntimeException(
+ "Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
+ });
+ InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
+ }
}
protected boolean shouldFailOnLeakedReceiver() {
@@ -209,7 +283,11 @@
}
public static boolean isRobolectricTest() {
- return Build.FINGERPRINT.contains("robolectric");
+ return !isRavenwoodTest() && Build.FINGERPRINT.contains("robolectric");
+ }
+
+ public static boolean isRavenwoodTest() {
+ return RavenwoodRule.isOnRavenwood();
}
private static final void validateThread(Looper l) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
index d89d7b0..364d3b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
@@ -34,10 +34,16 @@
// is missing (constructing the actual one would throw).
// TODO(b/219008720): Remove this.
dependency.injectMockDependency(SystemUIDialogManager::class.java)
- dependency.injectTestDependency(
- DialogLaunchAnimator::class.java,
- fakeDialogLaunchAnimator()
- )
+
+ // TODO(b/292141694): build out Ravenwood support for UI animations
+ // Ravenwood doesn't yet provide UI animations, so we sidestep this global configuration
+ // step; any tests that rely on it are already being excluded under Ravenwood
+ if (!SysuiTestCase.isRavenwoodTest()) {
+ dependency.injectTestDependency(
+ DialogLaunchAnimator::class.java,
+ fakeDialogLaunchAnimator()
+ )
+ }
// Many tests end up creating a BroadcastDispatcher. Instead, give them a fake that will
// record receivers registered. They are not actually leaked as they are kept just as a weak