• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.android.launcher3.ui;
17 
18 import static androidx.test.InstrumentationRegistry.getInstrumentation;
19 
20 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
21 
22 import static org.junit.Assert.assertTrue;
23 
24 import android.content.ComponentName;
25 import android.content.Intent;
26 import android.os.Process;
27 import android.system.OsConstants;
28 import android.util.Log;
29 
30 import androidx.test.uiautomator.By;
31 import androidx.test.uiautomator.BySelector;
32 import androidx.test.uiautomator.Until;
33 
34 import com.android.launcher3.Launcher;
35 import com.android.launcher3.LauncherState;
36 import com.android.launcher3.Utilities;
37 import com.android.launcher3.tapl.LauncherInstrumentation;
38 import com.android.launcher3.tapl.TestHelpers;
39 import com.android.launcher3.testcomponent.TestCommandReceiver;
40 import com.android.launcher3.util.LooperExecutor;
41 import com.android.launcher3.util.TestUtil;
42 import com.android.launcher3.util.Wait;
43 import com.android.launcher3.util.rule.FailureWatcher;
44 import com.android.launcher3.util.rule.ShellCommandRule;
45 import com.android.launcher3.util.rule.TestIsolationRule;
46 import com.android.launcher3.util.rule.ViewCaptureRule;
47 
48 import org.junit.rules.RuleChain;
49 import org.junit.rules.TestRule;
50 
51 import java.util.Objects;
52 import java.util.concurrent.Callable;
53 import java.util.concurrent.TimeUnit;
54 import java.util.concurrent.TimeoutException;
55 import java.util.function.Consumer;
56 import java.util.function.Function;
57 import java.util.function.Supplier;
58 
59 /**
60  * Base class for all instrumentation tests providing various utility methods.
61  */
62 public abstract class AbstractLauncherUiTest<LAUNCHER_TYPE extends Launcher>
63         extends BaseLauncherTaplTest {
64 
65     private static final String TAG = "AbstractLauncherUiTest";
66 
67     protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
68 
AbstractLauncherUiTest()69     protected AbstractLauncherUiTest() {
70         if (TestHelpers.isInLauncherProcess()) {
71             Utilities.enableRunningInTestHarnessForTests();
72             mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
73                             TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString())
74                     .getString("result"));
75         }
76     }
77 
78     /**
79      * @deprecated call {@link #performInitialization} instead
80      */
81     @Deprecated
initialize(AbstractLauncherUiTest test)82     public static void initialize(AbstractLauncherUiTest test) throws Exception {
83         test.performInitialization();
84     }
85 
86     @Override
performInitialization()87     protected void performInitialization() {
88         reinitializeLauncherData();
89         mDevice.pressHome();
90         // Check that we switched to home.
91         mLauncher.getWorkspace();
92 
93         waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
94         waitForState("Launcher internal state didn't switch to Home",
95                 () -> LauncherState.NORMAL);
96         waitForResumed("Launcher internal state is still Background");
97 
98         checkDetectedLeaks(mLauncher, true);
99     }
100 
101     @Override
getRulesInsideActivityMonitor()102     protected TestRule getRulesInsideActivityMonitor() {
103         final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(
104                 Launcher.ACTIVITY_TRACKER::getCreatedContext);
105         final RuleChain inner = RuleChain
106                 .outerRule(new PortraitLandscapeRunner<>(this))
107                 .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData))
108                 // .around(viewCaptureRule) // b/315482167
109                 .around(new TestIsolationRule(mLauncher, true));
110 
111         return TestHelpers.isInLauncherProcess()
112                 ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
113                 : inner;
114     }
115 
116     /**
117      * Runs the callback on the UI thread and returns the result.
118      */
getOnUiThread(final Callable<T> callback)119     protected <T> T getOnUiThread(final Callable<T> callback) {
120         try {
121             return mMainThreadExecutor.submit(callback).get(TestUtil.DEFAULT_UI_TIMEOUT,
122                     TimeUnit.MILLISECONDS);
123         } catch (TimeoutException e) {
124             Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
125             Process.sendSignal(Process.myPid(), OsConstants.SIGABRT);
126             throw new RuntimeException(e);
127         } catch (Throwable e) {
128             throw new RuntimeException(e);
129         }
130     }
131 
getFromLauncher(Function<LAUNCHER_TYPE, T> f)132     protected <T> T getFromLauncher(Function<LAUNCHER_TYPE, T> f) {
133         if (!TestHelpers.isInLauncherProcess()) return null;
134         return getOnUiThread(() -> f.apply(Launcher.ACTIVITY_TRACKER.getCreatedContext()));
135     }
136 
executeOnLauncher(Consumer<LAUNCHER_TYPE> f)137     protected void executeOnLauncher(Consumer<LAUNCHER_TYPE> f) {
138         getFromLauncher(launcher -> {
139             f.accept(launcher);
140             return null;
141         });
142     }
143 
144     // Execute an action on Launcher, but forgive it when launcher is null.
145     // Launcher can be null if teardown is happening after a failed setup step where launcher
146     // activity failed to be created.
executeOnLauncherInTearDown(Consumer<LAUNCHER_TYPE> f)147     protected void executeOnLauncherInTearDown(Consumer<LAUNCHER_TYPE> f) {
148         executeOnLauncher(launcher -> {
149             if (launcher != null) f.accept(launcher);
150         });
151     }
152 
153     // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
154     // expecting the results of that gesture because the wait can hide flakeness.
waitForState(String message, Supplier<LauncherState> state)155     protected void waitForState(String message, Supplier<LauncherState> state) {
156         waitForLauncherCondition(message,
157                 launcher -> launcher.getStateManager().getCurrentStableState() == state.get());
158     }
159 
160     // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
161     // expecting the results of that gesture because the wait can hide flakeness.
waitForStateTransitionToEnd(String message, Supplier<LauncherState> state)162     protected void waitForStateTransitionToEnd(String message, Supplier<LauncherState> state) {
163         waitForLauncherCondition(message,
164                 launcher -> launcher.getStateManager().isInStableState(state.get())
165                         && !launcher.getStateManager().isInTransition());
166     }
167 
waitForResumed(String message)168     protected void waitForResumed(String message) {
169         waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed());
170     }
171 
172     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
173     // flakiness.
waitForLauncherCondition(String message, Function<LAUNCHER_TYPE, Boolean> condition)174     protected void waitForLauncherCondition(String
175             message, Function<LAUNCHER_TYPE, Boolean> condition) {
176         waitForLauncherCondition(message, condition, TestUtil.DEFAULT_UI_TIMEOUT);
177     }
178 
179     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
180     // flakiness.
waitForLauncherCondition( String message, Function<LAUNCHER_TYPE, Boolean> condition, long timeout)181     protected void waitForLauncherCondition(
182             String message, Function<LAUNCHER_TYPE, Boolean> condition, long timeout) {
183         verifyKeyguardInvisible();
184         if (!TestHelpers.isInLauncherProcess()) return;
185         Wait.atMost(message, () -> getFromLauncher(condition), mLauncher, timeout);
186     }
187 
188     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
189     // flakiness.
getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f)190     protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f) {
191         if (!TestHelpers.isInLauncherProcess()) return null;
192 
193         final Object[] output = new Object[1];
194         Wait.atMost(message, () -> {
195             final Object fromLauncher = getFromLauncher(f);
196             output[0] = fromLauncher;
197             return fromLauncher != null;
198         }, mLauncher);
199         return (T) output[0];
200     }
201 
202     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
203     // flakiness.
waitForLauncherCondition( String message, Runnable testThreadAction, Function<LAUNCHER_TYPE, Boolean> condition, long timeout)204     protected void waitForLauncherCondition(
205             String message,
206             Runnable testThreadAction, Function<LAUNCHER_TYPE, Boolean> condition,
207             long timeout) {
208         if (!TestHelpers.isInLauncherProcess()) return;
209         Wait.atMost(message, () -> {
210             testThreadAction.run();
211             return getFromLauncher(condition);
212         }, mLauncher, timeout);
213     }
214 
startAppFast(String packageName)215     public static void startAppFast(String packageName) {
216         startIntent(
217                 getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
218                         packageName),
219                 By.pkg(packageName).depth(0),
220                 true /* newTask */);
221     }
222 
startTestActivity(String activityName, String activityLabel)223     public static void startTestActivity(String activityName, String activityLabel) {
224         final String packageName = getAppPackageName();
225         final Intent intent = getInstrumentation().getContext().getPackageManager().
226                 getLaunchIntentForPackage(packageName);
227         intent.setComponent(new ComponentName(packageName,
228                 "com.android.launcher3.tests." + activityName));
229         startIntent(intent, By.pkg(packageName).text(activityLabel),
230                 false /* newTask */);
231     }
232 
startTestActivity(int activityNumber)233     public static void startTestActivity(int activityNumber) {
234         startTestActivity("Activity" + activityNumber, "TestActivity" + activityNumber);
235     }
236 
startImeTestActivity()237     public static void startImeTestActivity() {
238         final String packageName = getAppPackageName();
239         final Intent intent = getInstrumentation().getContext().getPackageManager().
240                 getLaunchIntentForPackage(packageName);
241         intent.setComponent(new ComponentName(packageName,
242                 "com.android.launcher3.testcomponent.ImeTestActivity"));
243         startIntent(intent, By.pkg(packageName).text("ImeTestActivity"),
244                 false /* newTask */);
245     }
246 
247     /** Starts ExcludeFromRecentsTestActivity, which has excludeFromRecents="true". */
startExcludeFromRecentsTestActivity()248     public static void startExcludeFromRecentsTestActivity() {
249         final String packageName = getAppPackageName();
250         final Intent intent = getInstrumentation().getContext().getPackageManager()
251                 .getLaunchIntentForPackage(packageName);
252         intent.setComponent(new ComponentName(packageName,
253                 "com.android.launcher3.testcomponent.ExcludeFromRecentsTestActivity"));
254         startIntent(intent, By.pkg(packageName).text("ExcludeFromRecentsTestActivity"),
255                 false /* newTask */);
256     }
257 
startIntent(Intent intent, BySelector selector, boolean newTask)258     private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
259         intent.addCategory(Intent.CATEGORY_LAUNCHER);
260         if (newTask) {
261             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
262         } else {
263             intent.addFlags(
264                     Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
265         }
266         getInstrumentation().getTargetContext().startActivity(intent);
267         assertTrue("App didn't start: " + selector,
268                 TestHelpers.wait(Until.hasObject(selector), TestUtil.DEFAULT_UI_TIMEOUT));
269 
270         // Wait for the Launcher to stop.
271         final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation();
272         Wait.atMost("Launcher activity didn't stop",
273                 () -> !launcherInstrumentation.isLauncherActivityStarted(),
274                 launcherInstrumentation);
275     }
276 
277 
resolveSystemApp(String category)278     public static String resolveSystemApp(String category) {
279         return resolveSystemAppInfo(category).packageName;
280     }
281 
closeLauncherActivity()282     protected void closeLauncherActivity() {
283         // Destroy Launcher activity.
284         executeOnLauncher(launcher -> {
285             if (launcher != null) {
286                 onLauncherActivityClose(launcher);
287                 launcher.finish();
288             }
289         });
290         waitForLauncherCondition("Launcher still active", launcher -> launcher == null);
291     }
292 
isInLaunchedApp(LAUNCHER_TYPE launcher)293     protected boolean isInLaunchedApp(LAUNCHER_TYPE launcher) {
294         return launcher == null || !launcher.hasBeenResumed();
295     }
296 
isInState(Supplier<LauncherState> state)297     protected boolean isInState(Supplier<LauncherState> state) {
298         if (!TestHelpers.isInLauncherProcess()) return true;
299         return getFromLauncher(
300                 launcher -> launcher.getStateManager().getState() == state.get());
301     }
302 
getAllAppsScroll(LAUNCHER_TYPE launcher)303     protected int getAllAppsScroll(LAUNCHER_TYPE launcher) {
304         return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
305     }
306 
onLauncherActivityClose(LAUNCHER_TYPE launcher)307     protected void onLauncherActivityClose(LAUNCHER_TYPE launcher) {
308     }
309 }
310