Files
Lawnchair/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
T
Pun Butrach b67168f737 Allocate insane memory just for CI
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
2026-01-10 20:46:44 +07:00

310 lines
12 KiB
Java

/*
* Copyright (C) 2017 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.ui;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Process;
import android.system.OsConstants;
import android.util.Log;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Until;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.util.rule.TestIsolationRule;
import com.android.launcher3.util.rule.ViewCaptureRule;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Base class for all instrumentation tests providing various utility methods.
*/
public abstract class AbstractLauncherUiTest<LAUNCHER_TYPE extends Launcher>
extends BaseLauncherTaplTest {
private static final String TAG = "AbstractLauncherUiTest";
protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
protected AbstractLauncherUiTest() {
if (TestHelpers.isInLauncherProcess()) {
Utilities.enableRunningInTestHarnessForTests();
mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString())
.getString("result"));
}
}
/**
* @deprecated call {@link #performInitialization} instead
*/
@Deprecated
public static void initialize(AbstractLauncherUiTest test) throws Exception {
test.performInitialization();
}
@Override
protected void performInitialization() {
reinitializeLauncherData();
mDevice.pressHome();
// Check that we switched to home.
mLauncher.getWorkspace();
waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
waitForState("Launcher internal state didn't switch to Home",
() -> LauncherState.NORMAL);
waitForResumed("Launcher internal state is still Background");
checkDetectedLeaks(mLauncher, true);
}
@Override
protected TestRule getRulesInsideActivityMonitor() {
final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(
Launcher.ACTIVITY_TRACKER::getCreatedContext);
final RuleChain inner = RuleChain
.outerRule(new PortraitLandscapeRunner<>(this))
.around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData))
// .around(viewCaptureRule) // b/315482167
.around(new TestIsolationRule(mLauncher, true));
return TestHelpers.isInLauncherProcess()
? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
: inner;
}
/**
* Runs the callback on the UI thread and returns the result.
*/
protected <T> T getOnUiThread(final Callable<T> callback) {
try {
return mMainThreadExecutor.submit(callback).get(TestUtil.DEFAULT_UI_TIMEOUT,
TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
Process.sendSignal(Process.myPid(), OsConstants.SIGABRT);
throw new RuntimeException(e);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
protected <T> T getFromLauncher(Function<LAUNCHER_TYPE, T> f) {
if (!TestHelpers.isInLauncherProcess()) return null;
return getOnUiThread(() -> f.apply(Launcher.ACTIVITY_TRACKER.getCreatedContext()));
}
protected void executeOnLauncher(Consumer<LAUNCHER_TYPE> f) {
getFromLauncher(launcher -> {
f.accept(launcher);
return null;
});
}
// Execute an action on Launcher, but forgive it when launcher is null.
// Launcher can be null if teardown is happening after a failed setup step where launcher
// activity failed to be created.
protected void executeOnLauncherInTearDown(Consumer<LAUNCHER_TYPE> f) {
executeOnLauncher(launcher -> {
if (launcher != null) f.accept(launcher);
});
}
// Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
// expecting the results of that gesture because the wait can hide flakeness.
protected void waitForState(String message, Supplier<LauncherState> state) {
waitForLauncherCondition(message,
launcher -> launcher.getStateManager().getCurrentStableState() == state.get());
}
// Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
// expecting the results of that gesture because the wait can hide flakeness.
protected void waitForStateTransitionToEnd(String message, Supplier<LauncherState> state) {
waitForLauncherCondition(message,
launcher -> launcher.getStateManager().isInStableState(state.get())
&& !launcher.getStateManager().isInTransition());
}
protected void waitForResumed(String message) {
waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed());
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
protected void waitForLauncherCondition(String
message, Function<LAUNCHER_TYPE, Boolean> condition) {
waitForLauncherCondition(message, condition, TestUtil.DEFAULT_UI_TIMEOUT);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
protected void waitForLauncherCondition(
String message, Function<LAUNCHER_TYPE, Boolean> condition, long timeout) {
verifyKeyguardInvisible();
if (!TestHelpers.isInLauncherProcess()) return;
Wait.atMost(message, () -> getFromLauncher(condition), mLauncher, timeout);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f) {
if (!TestHelpers.isInLauncherProcess()) return null;
final Object[] output = new Object[1];
Wait.atMost(message, () -> {
final Object fromLauncher = getFromLauncher(f);
output[0] = fromLauncher;
return fromLauncher != null;
}, mLauncher);
return (T) output[0];
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
protected void waitForLauncherCondition(
String message,
Runnable testThreadAction, Function<LAUNCHER_TYPE, Boolean> condition,
long timeout) {
if (!TestHelpers.isInLauncherProcess()) return;
Wait.atMost(message, () -> {
testThreadAction.run();
return getFromLauncher(condition);
}, mLauncher, timeout);
}
public static void startAppFast(String packageName) {
startIntent(
getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
packageName),
By.pkg(packageName).depth(0),
true /* newTask */);
}
public static void startTestActivity(String activityName, String activityLabel) {
final String packageName = getAppPackageName();
final Intent intent = getInstrumentation().getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
intent.setComponent(new ComponentName(packageName,
"com.android.launcher3.tests." + activityName));
startIntent(intent, By.pkg(packageName).text(activityLabel),
false /* newTask */);
}
public static void startTestActivity(int activityNumber) {
startTestActivity("Activity" + activityNumber, "TestActivity" + activityNumber);
}
public static void startImeTestActivity() {
final String packageName = getAppPackageName();
final Intent intent = getInstrumentation().getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
intent.setComponent(new ComponentName(packageName,
"com.android.launcher3.testcomponent.ImeTestActivity"));
startIntent(intent, By.pkg(packageName).text("ImeTestActivity"),
false /* newTask */);
}
/** Starts ExcludeFromRecentsTestActivity, which has excludeFromRecents="true". */
public static void startExcludeFromRecentsTestActivity() {
final String packageName = getAppPackageName();
final Intent intent = getInstrumentation().getContext().getPackageManager()
.getLaunchIntentForPackage(packageName);
intent.setComponent(new ComponentName(packageName,
"com.android.launcher3.testcomponent.ExcludeFromRecentsTestActivity"));
startIntent(intent, By.pkg(packageName).text("ExcludeFromRecentsTestActivity"),
false /* newTask */);
}
private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
if (newTask) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
} else {
intent.addFlags(
Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
}
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
TestHelpers.wait(Until.hasObject(selector), TestUtil.DEFAULT_UI_TIMEOUT));
// Wait for the Launcher to stop.
final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation();
Wait.atMost("Launcher activity didn't stop",
() -> !launcherInstrumentation.isLauncherActivityStarted(),
launcherInstrumentation);
}
public static String resolveSystemApp(String category) {
return resolveSystemAppInfo(category).packageName;
}
protected void closeLauncherActivity() {
// Destroy Launcher activity.
executeOnLauncher(launcher -> {
if (launcher != null) {
onLauncherActivityClose(launcher);
launcher.finish();
}
});
waitForLauncherCondition("Launcher still active", launcher -> launcher == null);
}
protected boolean isInLaunchedApp(LAUNCHER_TYPE launcher) {
return launcher == null || !launcher.hasBeenResumed();
}
protected boolean isInState(Supplier<LauncherState> state) {
if (!TestHelpers.isInLauncherProcess()) return true;
return getFromLauncher(
launcher -> launcher.getStateManager().getState() == state.get());
}
protected int getAllAppsScroll(LAUNCHER_TYPE launcher) {
return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
}
protected void onLauncherActivityClose(LAUNCHER_TYPE launcher) {
}
}