Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b319177 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

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
parent 1a84549b
Loading
Loading
Loading
Loading
+31 −1
Original line number Diff line number Diff line
@@ -330,6 +330,7 @@ android_library {
        "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_library {

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_robolectric_test {
    ],
}

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`.
+5 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.content.res.Configuration;
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 @@ public class SystemUIDialogTest extends SysuiTestCase {
    @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() {
+91 −13
Original line number Diff line number Diff line
@@ -21,17 +21,25 @@ import static org.mockito.Mockito.spy;
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 com.android.systemui.flags.SceneContainerRule;
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 abstract class SysuiTestCase {
    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 @@ public abstract class SysuiTestCase {
    @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 @@ public abstract class SysuiTestCase {
        }
    }

    @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,18 +172,23 @@ public abstract class SysuiTestCase {
    public void SysuiSetup() throws Exception {
        mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
        mDependency = mSysuiDependency.install();
        // 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(
                    "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
                        "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");
                        "Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
            });
            InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
        }
    }

    protected boolean shouldFailOnLeakedReceiver() {
        return false;
@@ -209,7 +283,11 @@ public abstract class SysuiTestCase {
    }

    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) {
+10 −4
Original line number Diff line number Diff line
@@ -34,10 +34,16 @@ class SysuiTestDependency(
        // is missing (constructing the actual one would throw).
        // TODO(b/219008720): Remove this.
        dependency.injectMockDependency(SystemUIDialogManager::class.java)

        // 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