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