• 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.ui.TaplTestsLauncher3.getAppPackageName;
21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.ActivityInfo;
32 import android.content.pm.LauncherActivityInfo;
33 import android.content.pm.LauncherApps;
34 import android.content.pm.PackageInfo;
35 import android.content.pm.PackageManager;
36 import android.graphics.Point;
37 import android.os.Debug;
38 import android.os.Process;
39 import android.os.RemoteException;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.system.OsConstants;
43 import android.util.Log;
44 
45 import androidx.test.InstrumentationRegistry;
46 import androidx.test.uiautomator.By;
47 import androidx.test.uiautomator.BySelector;
48 import androidx.test.uiautomator.UiDevice;
49 import androidx.test.uiautomator.Until;
50 
51 import com.android.launcher3.Launcher;
52 import com.android.launcher3.LauncherAppState;
53 import com.android.launcher3.LauncherSettings;
54 import com.android.launcher3.LauncherState;
55 import com.android.launcher3.Utilities;
56 import com.android.launcher3.model.data.ItemInfo;
57 import com.android.launcher3.statemanager.StateManager;
58 import com.android.launcher3.tapl.HomeAllApps;
59 import com.android.launcher3.tapl.HomeAppIcon;
60 import com.android.launcher3.tapl.LauncherInstrumentation;
61 import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
62 import com.android.launcher3.tapl.TestHelpers;
63 import com.android.launcher3.testcomponent.TestCommandReceiver;
64 import com.android.launcher3.testing.shared.TestProtocol;
65 import com.android.launcher3.util.LooperExecutor;
66 import com.android.launcher3.util.SimpleBroadcastReceiver;
67 import com.android.launcher3.util.Wait;
68 import com.android.launcher3.util.WidgetUtils;
69 import com.android.launcher3.util.rule.FailureWatcher;
70 import com.android.launcher3.util.rule.LauncherActivityRule;
71 import com.android.launcher3.util.rule.SamplerRule;
72 import com.android.launcher3.util.rule.ScreenRecordRule;
73 import com.android.launcher3.util.rule.ShellCommandRule;
74 import com.android.launcher3.util.rule.TestStabilityRule;
75 
76 import org.junit.After;
77 import org.junit.Assert;
78 import org.junit.Before;
79 import org.junit.Rule;
80 import org.junit.rules.RuleChain;
81 import org.junit.rules.TestRule;
82 
83 import java.io.IOException;
84 import java.lang.annotation.ElementType;
85 import java.lang.annotation.Retention;
86 import java.lang.annotation.RetentionPolicy;
87 import java.lang.annotation.Target;
88 import java.util.concurrent.Callable;
89 import java.util.concurrent.CountDownLatch;
90 import java.util.concurrent.TimeUnit;
91 import java.util.concurrent.TimeoutException;
92 import java.util.function.Consumer;
93 import java.util.function.Function;
94 import java.util.function.Supplier;
95 
96 /**
97  * Base class for all instrumentation tests providing various utility methods.
98  */
99 public abstract class AbstractLauncherUiTest {
100 
101     public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
102     public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
103 
104     public static final long DEFAULT_UI_TIMEOUT = 10000;
105     private static final String TAG = "AbstractLauncherUiTest";
106 
107     private static boolean sDumpWasGenerated = false;
108     private static boolean sActivityLeakReported = false;
109     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
110 
111     protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
112     protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
113     protected final LauncherInstrumentation mLauncher = new LauncherInstrumentation();
114     protected Context mTargetContext;
115     protected String mTargetPackage;
116     private int mLauncherPid;
117 
checkDetectedLeaks(LauncherInstrumentation launcher)118     public static void checkDetectedLeaks(LauncherInstrumentation launcher) {
119         if (sActivityLeakReported) return;
120 
121         // Check whether activity leak detector has found leaked activities.
122         Wait.atMost(() -> getActivityLeakErrorMessage(launcher),
123                 () -> {
124                     launcher.forceGc();
125                     return MAIN_EXECUTOR.submit(
126                             () -> launcher.noLeakedActivities()).get();
127                 }, DEFAULT_UI_TIMEOUT, launcher);
128     }
129 
getActivityLeakErrorMessage(LauncherInstrumentation launcher)130     private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher) {
131         sActivityLeakReported = true;
132         return "Activity leak detector has found leaked activities, "
133                 + dumpHprofData(launcher, false) + ".";
134     }
135 
dumpHprofData(LauncherInstrumentation launcher, boolean intentionalLeak)136     public static String dumpHprofData(LauncherInstrumentation launcher, boolean intentionalLeak) {
137         if (intentionalLeak) return "intentional leak; not generating dump";
138 
139         String result;
140         if (sDumpWasGenerated) {
141             result = "dump has already been generated by another test";
142         } else {
143             try {
144                 final String fileName =
145                         getInstrumentation().getTargetContext().getFilesDir().getPath()
146                                 + "/ActivityLeakHeapDump.hprof";
147                 if (TestHelpers.isInLauncherProcess()) {
148                     Debug.dumpHprofData(fileName);
149                 } else {
150                     final UiDevice device = UiDevice.getInstance(getInstrumentation());
151                     device.executeShellCommand(
152                             "am dumpheap " + device.getLauncherPackageName() + " " + fileName);
153                 }
154                 Log.d(TAG, "Saved leak dump, the leak is still present: "
155                         + !launcher.noLeakedActivities());
156                 sDumpWasGenerated = true;
157                 result = "saved memory dump as an artifact";
158             } catch (Throwable e) {
159                 Log.e(TAG, "dumpHprofData failed", e);
160                 result = "failed to save memory dump";
161             }
162         }
163         return result + ". Full list of activities: " + launcher.getRootedActivitiesList();
164     }
165 
AbstractLauncherUiTest()166     protected AbstractLauncherUiTest() {
167         mLauncher.enableCheckEventsForSuccessfulGestures();
168         try {
169             mDevice.setOrientationNatural();
170         } catch (RemoteException e) {
171             throw new RuntimeException(e);
172         }
173         if (TestHelpers.isInLauncherProcess()) {
174             Utilities.enableRunningInTestHarnessForTests();
175             mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
176                     TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
177                     getString("result"));
178             mLauncher.setOnSettledStateAction(
179                     containerType -> executeOnLauncher(
180                             launcher ->
181                                     checkLauncherIntegrity(launcher, containerType)));
182         }
183         mLauncher.enableDebugTracing();
184         // Avoid double-reporting of Launcher crashes.
185         mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0);
186     }
187 
188     protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
189 
190     @Rule
191     public ShellCommandRule mDisableHeadsUpNotification =
192             ShellCommandRule.disableHeadsUpNotification();
193 
194     @Rule
195     public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
196 
clearPackageData(String pkg)197     protected void clearPackageData(String pkg) throws IOException, InterruptedException {
198         final CountDownLatch count = new CountDownLatch(2);
199         final SimpleBroadcastReceiver broadcastReceiver =
200                 new SimpleBroadcastReceiver(i -> count.countDown());
201         broadcastReceiver.registerPkgActions(mTargetContext, pkg,
202                         Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED);
203 
204         mDevice.executeShellCommand("pm clear " + pkg);
205         assertTrue(pkg + " didn't restart", count.await(10, TimeUnit.SECONDS));
206         mTargetContext.unregisterReceiver(broadcastReceiver);
207     }
208 
209     // Annotation for tests that need to be run in portrait and landscape modes.
210     @Retention(RetentionPolicy.RUNTIME)
211     @Target(ElementType.METHOD)
212     protected @interface PortraitLandscape {
213     }
214 
getRulesInsideActivityMonitor()215     protected TestRule getRulesInsideActivityMonitor() {
216         final RuleChain inner = RuleChain
217                 .outerRule(new PortraitLandscapeRunner(this))
218                 .around(new FailureWatcher(mDevice, mLauncher));
219 
220         return TestHelpers.isInLauncherProcess()
221                 ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher())
222                 .around(inner) :
223                 inner;
224     }
225 
226     @Rule
227     public TestRule mOrderSensitiveRules = RuleChain
228             .outerRule(new SamplerRule())
229             .around(new TestStabilityRule())
230             .around(mActivityMonitor)
231             .around(getRulesInsideActivityMonitor());
232 
getDevice()233     public UiDevice getDevice() {
234         return mDevice;
235     }
236 
237     @Before
setUp()238     public void setUp() throws Exception {
239         mLauncher.onTestStart();
240         Assert.assertTrue("Keyguard is visible, which is likely caused by a crash in SysUI",
241                 TestHelpers.wait(
242                         Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
243 
244         final String launcherPackageName = mDevice.getLauncherPackageName();
245         try {
246             final Context context = InstrumentationRegistry.getContext();
247             final PackageManager pm = context.getPackageManager();
248             final PackageInfo launcherPackage = pm.getPackageInfo(launcherPackageName, 0);
249 
250             if (!launcherPackage.versionName.equals("BuildFromAndroidStudio")) {
251                 Assert.assertEquals("Launcher version doesn't match tests version",
252                         pm.getPackageInfo(context.getPackageName(), 0).getLongVersionCode(),
253                         launcherPackage.getLongVersionCode());
254             }
255         } catch (PackageManager.NameNotFoundException e) {
256             throw new RuntimeException(e);
257         }
258 
259         mLauncherPid = 0;
260 
261         mTargetContext = InstrumentationRegistry.getTargetContext();
262         mTargetPackage = mTargetContext.getPackageName();
263         mLauncherPid = mLauncher.getPid();
264 
265         UserManager userManager = mTargetContext.getSystemService(UserManager.class);
266         if (userManager != null) {
267             for (UserHandle userHandle : userManager.getUserProfiles()) {
268                 if (!userHandle.isSystem()) {
269                     mDevice.executeShellCommand("pm remove-user " + userHandle.getIdentifier());
270                 }
271             }
272         }
273     }
274 
275     @After
verifyLauncherState()276     public void verifyLauncherState() {
277         try {
278             // Limits UI tests affecting tests running after them.
279             mLauncher.waitForLauncherInitialized();
280             if (mLauncherPid != 0) {
281                 assertEquals("Launcher crashed, pid mismatch:",
282                         mLauncherPid, mLauncher.getPid().intValue());
283             }
284         } finally {
285             mLauncher.onTestFinish();
286         }
287     }
288 
clearLauncherData()289     protected void clearLauncherData() {
290         mLauncher.clearLauncherData();
291         mLauncher.waitForLauncherInitialized();
292     }
293 
294     /**
295      * Removes all icons from homescreen and hotseat.
296      */
clearHomescreen()297     public void clearHomescreen() {
298         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
299                 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
300         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
301                 LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
302         resetLoaderState();
303     }
304 
resetLoaderState()305     protected void resetLoaderState() {
306         try {
307             mMainThreadExecutor.execute(
308                     () -> LauncherAppState.getInstance(
309                             mTargetContext).getModel().forceReload());
310         } catch (Throwable t) {
311             throw new IllegalArgumentException(t);
312         }
313         mLauncher.waitForLauncherInitialized();
314     }
315 
316     /**
317      * Adds {@param item} on the homescreen on the 0th screen
318      */
addItemToScreen(ItemInfo item)319     public void addItemToScreen(ItemInfo item) {
320         WidgetUtils.addItemToScreen(item, mTargetContext);
321         resetLoaderState();
322 
323         // Launch the home activity
324         mDevice.pressHome();
325         mLauncher.waitForLauncherInitialized();
326     }
327 
328     /**
329      * Runs the callback on the UI thread and returns the result.
330      */
getOnUiThread(final Callable<T> callback)331     protected <T> T getOnUiThread(final Callable<T> callback) {
332         try {
333             return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT,
334                     TimeUnit.MILLISECONDS);
335         } catch (TimeoutException e) {
336             Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
337             Process.sendSignal(Process.myPid(), OsConstants.SIGABRT);
338             throw new RuntimeException(e);
339         } catch (Throwable e) {
340             throw new RuntimeException(e);
341         }
342     }
343 
getFromLauncher(Function<Launcher, T> f)344     protected <T> T getFromLauncher(Function<Launcher, T> f) {
345         if (!TestHelpers.isInLauncherProcess()) return null;
346         return getOnUiThread(() -> f.apply(mActivityMonitor.getActivity()));
347     }
348 
executeOnLauncher(Consumer<Launcher> f)349     protected void executeOnLauncher(Consumer<Launcher> f) {
350         getFromLauncher(launcher -> {
351             f.accept(launcher);
352             return null;
353         });
354     }
355 
356     // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
357     // expecting the results of that gesture because the wait can hide flakeness.
waitForState(String message, Supplier<LauncherState> state)358     protected void waitForState(String message, Supplier<LauncherState> state) {
359         waitForLauncherCondition(message,
360                 launcher -> launcher.getStateManager().getCurrentStableState() == state.get());
361     }
362 
363     // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
364     // expecting the results of that gesture because the wait can hide flakeness.
waitForStateTransitionToEnd(String message, Supplier<LauncherState> state)365     protected void waitForStateTransitionToEnd(String message, Supplier<LauncherState> state) {
366         waitForLauncherCondition(message,
367                 launcher -> launcher.getStateManager().isInStableState(state.get())
368                         && !launcher.getStateManager().isInTransition());
369     }
370 
waitForResumed(String message)371     protected void waitForResumed(String message) {
372         waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed());
373     }
374 
375     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
376     // flakiness.
waitForLauncherCondition(String message, Function<Launcher, Boolean> condition)377     protected void waitForLauncherCondition(String
378             message, Function<Launcher, Boolean> condition) {
379         waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
380     }
381 
382     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
383     // flakiness.
getOnceNotNull(String message, Function<Launcher, T> f)384     protected <T> T getOnceNotNull(String message, Function<Launcher, T> f) {
385         return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT);
386     }
387 
388     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
389     // flakiness.
waitForLauncherCondition( String message, Function<Launcher, Boolean> condition, long timeout)390     protected void waitForLauncherCondition(
391             String message, Function<Launcher, Boolean> condition, long timeout) {
392         if (!TestHelpers.isInLauncherProcess()) return;
393         Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher);
394     }
395 
396     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
397     // flakiness.
getOnceNotNull(String message, Function<Launcher, T> f, long timeout)398     protected <T> T getOnceNotNull(String message, Function<Launcher, T> f, long timeout) {
399         if (!TestHelpers.isInLauncherProcess()) return null;
400 
401         final Object[] output = new Object[1];
402         Wait.atMost(message, () -> {
403             final Object fromLauncher = getFromLauncher(f);
404             output[0] = fromLauncher;
405             return fromLauncher != null;
406         }, timeout, mLauncher);
407         return (T) output[0];
408     }
409 
410     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
411     // flakiness.
waitForLauncherCondition( String message, Runnable testThreadAction, Function<Launcher, Boolean> condition, long timeout)412     protected void waitForLauncherCondition(
413             String message,
414             Runnable testThreadAction, Function<Launcher, Boolean> condition,
415             long timeout) {
416         if (!TestHelpers.isInLauncherProcess()) return;
417         Wait.atMost(message, () -> {
418             testThreadAction.run();
419             return getFromLauncher(condition);
420         }, timeout, mLauncher);
421     }
422 
getSettingsApp()423     protected LauncherActivityInfo getSettingsApp() {
424         return mTargetContext.getSystemService(LauncherApps.class)
425                 .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
426     }
427 
428     /**
429      * Broadcast receiver which blocks until the result is received.
430      */
431     public class BlockingBroadcastReceiver extends BroadcastReceiver {
432 
433         private final CountDownLatch latch = new CountDownLatch(1);
434         private Intent mIntent;
435 
BlockingBroadcastReceiver(String action)436         public BlockingBroadcastReceiver(String action) {
437             mTargetContext.registerReceiver(this, new IntentFilter(action));
438         }
439 
440         @Override
onReceive(Context context, Intent intent)441         public void onReceive(Context context, Intent intent) {
442             mIntent = intent;
443             latch.countDown();
444         }
445 
blockingGetIntent()446         public Intent blockingGetIntent() throws InterruptedException {
447             latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
448             mTargetContext.unregisterReceiver(this);
449             return mIntent;
450         }
451 
blockingGetExtraIntent()452         public Intent blockingGetExtraIntent() throws InterruptedException {
453             Intent intent = blockingGetIntent();
454             return intent == null ? null : (Intent) intent.getParcelableExtra(
455                     Intent.EXTRA_INTENT);
456         }
457     }
458 
startAppFast(String packageName)459     public static void startAppFast(String packageName) {
460         startIntent(
461                 getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
462                         packageName),
463                 By.pkg(packageName).depth(0),
464                 true /* newTask */);
465     }
466 
startTestActivity(int activityNumber)467     public static void startTestActivity(int activityNumber) {
468         final String packageName = getAppPackageName();
469         final Intent intent = getInstrumentation().getContext().getPackageManager().
470                 getLaunchIntentForPackage(packageName);
471         intent.setComponent(new ComponentName(packageName,
472                 "com.android.launcher3.tests.Activity" + activityNumber));
473         startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber),
474                 false /* newTask */);
475     }
476 
startImeTestActivity()477     public static void startImeTestActivity() {
478         final String packageName = getAppPackageName();
479         final Intent intent = getInstrumentation().getContext().getPackageManager().
480                 getLaunchIntentForPackage(packageName);
481         intent.setComponent(new ComponentName(packageName,
482                 "com.android.launcher3.testcomponent.ImeTestActivity"));
483         startIntent(intent, By.pkg(packageName).text("ImeTestActivity"),
484                 false /* newTask */);
485     }
486 
startIntent(Intent intent, BySelector selector, boolean newTask)487     private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
488         intent.addCategory(Intent.CATEGORY_LAUNCHER);
489         if (newTask) {
490             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
491         } else {
492             intent.addFlags(
493                     Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
494         }
495         getInstrumentation().getTargetContext().startActivity(intent);
496         assertTrue("App didn't start: " + selector,
497                 TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
498     }
499 
resolveSystemAppInfo(String category)500     public static ActivityInfo resolveSystemAppInfo(String category) {
501         return getInstrumentation().getContext().getPackageManager().resolveActivity(
502                 new Intent(Intent.ACTION_MAIN).addCategory(category),
503                 PackageManager.MATCH_SYSTEM_ONLY).
504                 activityInfo;
505     }
506 
507 
resolveSystemApp(String category)508     public static String resolveSystemApp(String category) {
509         return resolveSystemAppInfo(category).packageName;
510     }
511 
closeLauncherActivity()512     protected void closeLauncherActivity() {
513         // Destroy Launcher activity.
514         executeOnLauncher(launcher -> {
515             if (launcher != null) {
516                 onLauncherActivityClose(launcher);
517                 launcher.finish();
518             }
519         });
520         waitForLauncherCondition(
521                 "Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
522     }
523 
isInLaunchedApp(Launcher launcher)524     protected boolean isInLaunchedApp(Launcher launcher) {
525         return launcher == null || !launcher.hasBeenResumed();
526     }
527 
isInState(Supplier<LauncherState> state)528     protected boolean isInState(Supplier<LauncherState> state) {
529         if (!TestHelpers.isInLauncherProcess()) return true;
530         return getFromLauncher(
531                 launcher -> launcher.getStateManager().getState() == state.get());
532     }
533 
getAllAppsScroll(Launcher launcher)534     protected int getAllAppsScroll(Launcher launcher) {
535         return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
536     }
537 
checkLauncherIntegrity( Launcher launcher, ContainerType expectedContainerType)538     private void checkLauncherIntegrity(
539             Launcher launcher, ContainerType expectedContainerType) {
540         if (launcher != null) {
541             final StateManager<LauncherState> stateManager = launcher.getStateManager();
542             final LauncherState stableState = stateManager.getCurrentStableState();
543 
544             assertTrue("Stable state != state: " + stableState.getClass().getSimpleName() + ", "
545                             + stateManager.getState().getClass().getSimpleName(),
546                     stableState == stateManager.getState());
547 
548             final boolean isResumed = launcher.hasBeenResumed();
549             final boolean isStarted = launcher.isStarted();
550             checkLauncherState(launcher, expectedContainerType, isResumed, isStarted);
551 
552             final int ordinal = stableState.ordinal;
553 
554             switch (expectedContainerType) {
555                 case WORKSPACE:
556                 case WIDGETS: {
557                     assertTrue(
558                             "Launcher is not resumed in state: " + expectedContainerType,
559                             isResumed);
560                     assertTrue(TestProtocol.stateOrdinalToString(ordinal),
561                             ordinal == TestProtocol.NORMAL_STATE_ORDINAL);
562                     break;
563                 }
564                 case HOME_ALL_APPS: {
565                     assertTrue(
566                             "Launcher is not resumed in state: " + expectedContainerType,
567                             isResumed);
568                     assertTrue(TestProtocol.stateOrdinalToString(ordinal),
569                             ordinal == TestProtocol.ALL_APPS_STATE_ORDINAL);
570                     break;
571                 }
572                 case OVERVIEW: {
573                     verifyOverviewState(launcher, expectedContainerType, isStarted, isResumed,
574                             ordinal, TestProtocol.OVERVIEW_STATE_ORDINAL);
575                     break;
576                 }
577                 case SPLIT_SCREEN_SELECT: {
578                     verifyOverviewState(launcher, expectedContainerType, isStarted, isResumed,
579                             ordinal, TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL);
580                     break;
581                 }
582                 case TASKBAR_ALL_APPS:
583                 case LAUNCHED_APP: {
584                     assertTrue("Launcher is resumed in state: " + expectedContainerType,
585                             !isResumed);
586                     assertTrue(TestProtocol.stateOrdinalToString(ordinal),
587                             ordinal == TestProtocol.NORMAL_STATE_ORDINAL);
588                     break;
589                 }
590                 default:
591                     throw new IllegalArgumentException(
592                             "Illegal container: " + expectedContainerType);
593             }
594         } else {
595             assertTrue(
596                     "Container type is not LAUNCHED_APP, TASKBAR_ALL_APPS "
597                             + "or FALLBACK_OVERVIEW: " + expectedContainerType,
598                     expectedContainerType == ContainerType.LAUNCHED_APP
599                             || expectedContainerType == ContainerType.TASKBAR_ALL_APPS
600                             || expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
601         }
602     }
603 
checkLauncherState(Launcher launcher, ContainerType expectedContainerType, boolean isResumed, boolean isStarted)604     protected void checkLauncherState(Launcher launcher, ContainerType expectedContainerType,
605             boolean isResumed, boolean isStarted) {
606         assertTrue("hasBeenResumed() != isStarted(), hasBeenResumed(): " + isResumed,
607                 isResumed == isStarted);
608         assertTrue("hasBeenResumed() != isUserActive(), hasBeenResumed(): " + isResumed,
609                 isResumed == launcher.isUserActive());
610     }
611 
checkLauncherStateInOverview(Launcher launcher, ContainerType expectedContainerType, boolean isStarted, boolean isResumed)612     protected void checkLauncherStateInOverview(Launcher launcher,
613             ContainerType expectedContainerType, boolean isStarted, boolean isResumed) {
614         assertTrue("Launcher is not resumed in state: " + expectedContainerType,
615                 isResumed);
616     }
617 
onLauncherActivityClose(Launcher launcher)618     protected void onLauncherActivityClose(Launcher launcher) {
619     }
620 
createShortcutInCenterIfNotExist(String name)621     protected HomeAppIcon createShortcutInCenterIfNotExist(String name) {
622         Point dimension = mLauncher.getWorkspace().getIconGridDimensions();
623         return createShortcutIfNotExist(name, dimension.x / 2, dimension.y / 2);
624     }
625 
createShortcutIfNotExist(String name, Point cellPosition)626     protected HomeAppIcon createShortcutIfNotExist(String name, Point cellPosition) {
627         return createShortcutIfNotExist(name, cellPosition.x, cellPosition.y);
628     }
629 
createShortcutIfNotExist(String name, int cellX, int cellY)630     protected HomeAppIcon createShortcutIfNotExist(String name, int cellX, int cellY) {
631         HomeAppIcon homeAppIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name);
632         if (homeAppIcon == null) {
633             HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
634             allApps.freeze();
635             try {
636                 allApps.getAppIcon(name).dragToWorkspace(cellX, cellY);
637             } finally {
638                 allApps.unfreeze();
639             }
640             homeAppIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name);
641         }
642         return homeAppIcon;
643     }
644 
verifyOverviewState(Launcher launcher, ContainerType expectedContainerType, boolean isStarted, boolean isResumed, int ordinal, int expectedOrdinal)645     private void verifyOverviewState(Launcher launcher, ContainerType expectedContainerType,
646             boolean isStarted, boolean isResumed, int ordinal, int expectedOrdinal) {
647         checkLauncherStateInOverview(launcher, expectedContainerType, isStarted, isResumed);
648         assertEquals(TestProtocol.stateOrdinalToString(ordinal), ordinal, expectedOrdinal);
649     }
650 }
651