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