Adding some utility test rules

> LasyActivityRule to be used by ActivityScenario
> Utility rule to easily override feature flags

Bug: 284202543
Test: Presubmit
flag: N/A
Change-Id: I95ae19bfb1d67a55941e7b8e5c47516a351b0d24
diff --git a/tests/src/com/android/launcher3/util/rule/LazyActivityRule.java b/tests/src/com/android/launcher3/util/rule/LazyActivityRule.java
new file mode 100644
index 0000000..6c300bb
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/LazyActivityRule.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 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.launcher3.util.rule;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ActivityScenario;
+
+import com.android.launcher3.Launcher;
+
+import org.junit.rules.ExternalResource;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Similar to {@code ActivityScenarioRule} but it creates the activity lazily when needed
+ */
+public class LazyActivityRule<A extends Activity> extends ExternalResource {
+
+    private final Supplier<ActivityScenario<A>> mScenarioSupplier;
+
+    @Nullable private ActivityScenario<A> mScenario;
+
+    /**
+     * Constructs LazyActivityScenarioRule for a given scenario provider.
+     */
+    public LazyActivityRule(Supplier<ActivityScenario<A>> supplier) {
+        mScenarioSupplier = supplier;
+    }
+
+    /**
+     * Resets the rule, such that the activity is in closed state
+     */
+    public synchronized void reset() {
+        if (mScenario != null) {
+            try {
+                mScenario.close();
+            } catch (AssertionError e) {
+                // Ignore errors during close
+            }
+        }
+        mScenario = null;
+    }
+
+    @Override
+    protected synchronized void after() {
+        reset();
+    }
+
+    /**
+     * Returns the scenario, creating one if it doesn't exist
+     */
+    public synchronized ActivityScenario<A> getScenario() {
+        if (mScenario == null) {
+            mScenario = mScenarioSupplier.get();
+        }
+        return mScenario;
+    }
+
+    /**
+     * Executes the function {@code f} on the activities main thread and returns the result
+     */
+    public <T> T getFromActivity(Function<A, T> f) {
+        AtomicReference<T> result = new AtomicReference<>();
+        getScenario().onActivity(a -> result.set(f.apply(a)));
+        return result.get();
+    }
+
+    /**
+     * Runs the provided function {@code f} on the activity if the scenario is already created
+     */
+    public synchronized void runOnActivity(Consumer<A> f) {
+        if (mScenario != null) {
+            mScenario.onActivity(f::accept);
+        }
+    }
+
+    /**
+     * Returns a {@link LazyActivityRule} for the Launcher activity
+     */
+    public static <T extends Launcher> LazyActivityRule<T> forLauncher() {
+        Context context = getInstrumentation().getTargetContext();
+        // Create the activity after the model setup is done.
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK
+                        | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | FLAG_ACTIVITY_NO_ANIMATION);
+        ResolveInfo ri = context.getPackageManager().resolveActivity(
+                new Intent(homeIntent).setPackage(context.getPackageName()), 0);
+        homeIntent.setComponent(ri.getComponentInfo().getComponentName());
+        return new LazyActivityRule<>(() -> ActivityScenario.launch(homeIntent));
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/WrapperRule.kt b/tests/src/com/android/launcher3/util/rule/WrapperRule.kt
new file mode 100644
index 0000000..290cc45
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/WrapperRule.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.launcher3.util.rule
+
+import com.android.launcher3.config.FeatureFlags.BooleanFlag
+import com.android.launcher3.config.FeatureFlags.IntFlag
+import com.android.launcher3.util.SafeCloseable
+import com.android.launcher3.util.TestUtil
+import java.util.function.Supplier
+import org.junit.rules.ExternalResource
+
+/** Simple rule which wraps any SafeCloseable object */
+class WrapperRule(private val overrideProvider: Supplier<SafeCloseable>) : ExternalResource() {
+
+    private lateinit var overrideClosable: SafeCloseable
+
+    override fun before() {
+        overrideClosable = overrideProvider.get()
+    }
+
+    override fun after() {
+        overrideClosable.close()
+    }
+
+    companion object {
+
+        fun BooleanFlag.overrideFlag(value: Boolean) = WrapperRule {
+            TestUtil.overrideFlag(this, value)
+        }
+
+        fun IntFlag.overrideFlag(value: Int) = WrapperRule { TestUtil.overrideFlag(this, value) }
+    }
+}